diff --git a/src/config/Block.cxx b/src/config/Block.cxx index bc451ad4d..107baa83f 100644 --- a/src/config/Block.cxx +++ b/src/config/Block.cxx @@ -143,3 +143,10 @@ ConfigBlock::GetBlockValue(const char *name, bool default_value) const return bp->GetBoolValue(); } + +void +ConfigBlock::ThrowWithNested() const +{ + std::throw_with_nested(FormatRuntimeError("Error in block on line %i", + line)); +} diff --git a/src/config/Block.hxx b/src/config/Block.hxx index 82fce9761..4f0428721 100644 --- a/src/config/Block.hxx +++ b/src/config/Block.hxx @@ -137,6 +137,14 @@ struct ConfigBlock { unsigned GetPositiveValue(const char *name, unsigned default_value) const; bool GetBlockValue(const char *name, bool default_value) const; + + /** + * Call this method in a "catch" block to throw a nested + * exception showing the location of this block in the + * configuration file. + */ + [[noreturn]] + void ThrowWithNested() const; }; #endif diff --git a/src/config/Data.hxx b/src/config/Data.hxx index 73068f2c7..36bd3df6c 100644 --- a/src/config/Data.hxx +++ b/src/config/Data.hxx @@ -121,6 +121,26 @@ struct ConfigData { ConfigBlock &MakeBlock(ConfigBlockOption option, const char *key, const char *value); + + /** + * Invoke the given function for each instance of the + * specified block. + * + * Exceptions thrown by the function will be nested in one + * that specifies the location of the block. + */ + template<typename F> + void WithEach(ConfigBlockOption option, F &&f) const { + for (const auto &block : GetBlockList(option)) { + block.SetUsed(); + + try { + f(block); + } catch (...) { + block.ThrowWithNested(); + } + } + } }; #endif diff --git a/src/neighbor/Glue.cxx b/src/neighbor/Glue.cxx index 6d3e8fa5d..183f030dd 100644 --- a/src/neighbor/Glue.cxx +++ b/src/neighbor/Glue.cxx @@ -48,24 +48,16 @@ void NeighborGlue::Init(const ConfigData &config, EventLoop &loop, NeighborListener &listener) { - for (const auto &block : config.GetBlockList(ConfigBlockOption::NEIGHBORS)) { - block.SetUsed(); + config.WithEach(ConfigBlockOption::NEIGHBORS, [&, this](const auto &block){ + const char *plugin_name = block.GetBlockValue("plugin"); + if (plugin_name == nullptr) + throw std::runtime_error("Missing \"plugin\" configuration"); - try { - const char *plugin_name = block.GetBlockValue("plugin"); - if (plugin_name == nullptr) - throw std::runtime_error("Missing \"plugin\" configuration"); - - explorers.emplace_front(plugin_name, - CreateNeighborExplorer(loop, - listener, - plugin_name, - block)); - } catch (...) { - std::throw_with_nested(FormatRuntimeError("Line %i: ", - block.line)); - } - } + explorers.emplace_front(plugin_name, + CreateNeighborExplorer(loop, listener, + plugin_name, + block)); + }); } void diff --git a/src/output/MultipleOutputs.cxx b/src/output/MultipleOutputs.cxx index b728d06c4..e5ac932dc 100644 --- a/src/output/MultipleOutputs.cxx +++ b/src/output/MultipleOutputs.cxx @@ -92,8 +92,7 @@ MultipleOutputs::Configure(EventLoop &event_loop, EventLoop &rt_event_loop, const AudioOutputDefaults defaults(config); FilterFactory filter_factory(config); - for (const auto &block : config.GetBlockList(ConfigBlockOption::AUDIO_OUTPUT)) { - block.SetUsed(); + config.WithEach(ConfigBlockOption::AUDIO_OUTPUT, [&, this](const auto &block){ auto output = LoadOutputControl(event_loop, rt_event_loop, replay_gain_config, mixer_listener, @@ -104,7 +103,7 @@ MultipleOutputs::Configure(EventLoop &event_loop, EventLoop &rt_event_loop, "names: %s", output->GetName()); outputs.emplace_back(std::move(output)); - } + }); if (outputs.empty()) { /* auto-detect device */