config/Data: add WithEach(ConfigBlockOption)

To improve error messages without making callers more complex.
This commit is contained in:
Max Kellermann 2022-07-12 21:02:49 +02:00
parent 89a18b49a7
commit ae4f4d3533
5 changed files with 46 additions and 20 deletions

View File

@ -143,3 +143,10 @@ ConfigBlock::GetBlockValue(const char *name, bool default_value) const
return bp->GetBoolValue(); return bp->GetBoolValue();
} }
void
ConfigBlock::ThrowWithNested() const
{
std::throw_with_nested(FormatRuntimeError("Error in block on line %i",
line));
}

View File

@ -137,6 +137,14 @@ struct ConfigBlock {
unsigned GetPositiveValue(const char *name, unsigned default_value) const; unsigned GetPositiveValue(const char *name, unsigned default_value) const;
bool GetBlockValue(const char *name, bool 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 #endif

View File

@ -121,6 +121,26 @@ struct ConfigData {
ConfigBlock &MakeBlock(ConfigBlockOption option, ConfigBlock &MakeBlock(ConfigBlockOption option,
const char *key, const char *value); 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 #endif

View File

@ -48,24 +48,16 @@ void
NeighborGlue::Init(const ConfigData &config, NeighborGlue::Init(const ConfigData &config,
EventLoop &loop, NeighborListener &listener) EventLoop &loop, NeighborListener &listener)
{ {
for (const auto &block : config.GetBlockList(ConfigBlockOption::NEIGHBORS)) { config.WithEach(ConfigBlockOption::NEIGHBORS, [&, this](const auto &block){
block.SetUsed(); const char *plugin_name = block.GetBlockValue("plugin");
if (plugin_name == nullptr)
throw std::runtime_error("Missing \"plugin\" configuration");
try { explorers.emplace_front(plugin_name,
const char *plugin_name = block.GetBlockValue("plugin"); CreateNeighborExplorer(loop, listener,
if (plugin_name == nullptr) plugin_name,
throw std::runtime_error("Missing \"plugin\" configuration"); block));
});
explorers.emplace_front(plugin_name,
CreateNeighborExplorer(loop,
listener,
plugin_name,
block));
} catch (...) {
std::throw_with_nested(FormatRuntimeError("Line %i: ",
block.line));
}
}
} }
void void

View File

@ -92,8 +92,7 @@ MultipleOutputs::Configure(EventLoop &event_loop, EventLoop &rt_event_loop,
const AudioOutputDefaults defaults(config); const AudioOutputDefaults defaults(config);
FilterFactory filter_factory(config); FilterFactory filter_factory(config);
for (const auto &block : config.GetBlockList(ConfigBlockOption::AUDIO_OUTPUT)) { config.WithEach(ConfigBlockOption::AUDIO_OUTPUT, [&, this](const auto &block){
block.SetUsed();
auto output = LoadOutputControl(event_loop, rt_event_loop, auto output = LoadOutputControl(event_loop, rt_event_loop,
replay_gain_config, replay_gain_config,
mixer_listener, mixer_listener,
@ -104,7 +103,7 @@ MultipleOutputs::Configure(EventLoop &event_loop, EventLoop &rt_event_loop,
"names: %s", output->GetName()); "names: %s", output->GetName());
outputs.emplace_back(std::move(output)); outputs.emplace_back(std::move(output));
} });
if (outputs.empty()) { if (outputs.empty()) {
/* auto-detect device */ /* auto-detect device */