Skip to content

Commit 7986ffe

Browse files
reuse visitField for conversions on intermediate types (#52)
* reuse visitField for conversions on intermediate types * add new name function and remove manual namespacing
1 parent b16730f commit 7986ffe

File tree

4 files changed

+114
-37
lines changed

4 files changed

+114
-37
lines changed

config_utilities/include/config_utilities/config.h

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,17 @@ namespace config {
5353
*/
5454
inline void name(const std::string& name) { internal::Visitor::visitName(name); }
5555

56+
/**
57+
* @brief Set the name of a config from the template parameter type.
58+
* @tparam T type to get the name from
59+
*
60+
* This is for cases where you may not have a static config struct name (i.e., with a templated config struct)
61+
*/
62+
template <typename T>
63+
void name() {
64+
internal::Visitor::visitName(internal::typeName<T>());
65+
}
66+
5667
/**
5768
* @brief Declare string-named fields of the config. This string will be used to get the configs field values during
5869
* creation, and for checking of validity.

config_utilities/include/config_utilities/internal/visitor.h

Lines changed: 12 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -104,8 +104,8 @@ struct Visitor {
104104
template <typename T, typename std::enable_if<!isConfig<T>(), bool>::type = true>
105105
static void visitField(T& field, const std::string& field_name, const std::string& unit);
106106

107-
// Non-config types with a conversion.
108-
template <typename Conversion, typename T, typename std::enable_if<!isConfig<T>(), bool>::type = true>
107+
// Types with a extra conversion.
108+
template <typename Conversion, typename T>
109109
static void visitField(T& field, const std::string& field_name, const std::string& unit);
110110

111111
// Single config types.
@@ -166,11 +166,16 @@ struct Visitor {
166166
template <typename ConfigT, typename std::enable_if<is_virtual_config<ConfigT>::value, bool>::type = true>
167167
static MetaData getDefaults(const ConfigT& config);
168168

169-
// Dispatch getting field input info from conversions.
170-
template <typename Conversion, typename std::enable_if<!hasFieldInputInfo<Conversion>(), bool>::type = true>
171-
static FieldInputInfo::Ptr getFieldInputInfo();
172-
template <typename Conversion, typename std::enable_if<hasFieldInputInfo<Conversion>(), bool>::type = true>
173-
static FieldInputInfo::Ptr getFieldInputInfo();
169+
// Dispatch populating field input info from conversions.
170+
template <typename Conversion,
171+
typename ConfigT,
172+
typename std::enable_if<!hasFieldInputInfo<Conversion>() || isConfig<ConfigT>(), bool>::type = true>
173+
static void getFieldInputInfo(const std::string& field_name);
174+
175+
template <typename Conversion,
176+
typename ConfigT,
177+
typename std::enable_if<hasFieldInputInfo<Conversion>() && !isConfig<ConfigT>(), bool>::type = true>
178+
static void getFieldInputInfo(const std::string& field_name);
174179

175180
// Computes the default values for all fields in the meta data. This assumes that the meta data is already created,
176181
// and the meta data was created from ConfigT.

config_utilities/include/config_utilities/internal/visitor_impl.hpp

Lines changed: 41 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -188,26 +188,16 @@ void Visitor::visitField(T& field, const std::string& field_name, const std::str
188188
}
189189

190190
// Visits a non-config field with conversion.
191-
template <typename Conversion, typename T, typename std::enable_if<!isConfig<T>(), bool>::type>
191+
template <typename Conversion, typename T>
192192
void Visitor::visitField(T& field, const std::string& field_name, const std::string& unit) {
193193
auto& visitor = Visitor::instance();
194194

195-
// record the field that we visited without storing the value
196-
// kGet and kGetDefaults will populate the value field
197-
auto& info = visitor.data.field_infos.emplace_back();
198-
info.name = field_name;
199-
info.unit = unit;
200-
201195
if (visitor.mode == Visitor::Mode::kSet) {
202196
std::string error;
203197
auto intermediate = Conversion::toIntermediate(field, error);
204198
error.clear(); // We don't care about setting up the intermediate just to get data.
205199

206-
info.was_parsed = YamlParser::fromYaml(visitor.data.data, field_name, intermediate, visitor.name_space, error);
207-
if (!error.empty()) {
208-
visitor.data.errors.emplace_back(new Warning(field_name, error));
209-
error.clear();
210-
}
200+
Visitor::visitField(intermediate, field_name, unit);
211201

212202
Conversion::fromIntermediate(intermediate, field, error);
213203
if (!error.empty()) {
@@ -218,23 +208,17 @@ void Visitor::visitField(T& field, const std::string& field_name, const std::str
218208
if (visitor.mode == Visitor::Mode::kGet || visitor.mode == Visitor::Mode::kGetDefaults ||
219209
visitor.mode == Visitor::Mode::kGetInfo) {
220210
std::string error;
221-
const auto intermediate = Conversion::toIntermediate(field, error);
211+
auto intermediate = Conversion::toIntermediate(field, error);
222212
if (!error.empty()) {
223213
visitor.data.errors.emplace_back(new Warning(field_name, error));
224214
error.clear();
225215
}
226-
YAML::Node node = YamlParser::toYaml(field_name, intermediate, visitor.name_space, error);
227-
mergeYamlNodes(visitor.data.data, node);
228-
// This stores a reference to the node in the data.
229-
info.value = lookupNamespace(node, joinNamespace(visitor.name_space, field_name));
230-
if (!error.empty()) {
231-
visitor.data.errors.emplace_back(new Warning(field_name, error));
232-
}
216+
217+
Visitor::visitField(intermediate, field_name, unit);
233218

234219
// Get type information if requested.
235220
if (visitor.mode == Visitor::Mode::kGetInfo) {
236-
auto input_info = Visitor::getFieldInputInfo<Conversion>();
237-
info.input_info = FieldInputInfo::merge(input_info, info.input_info);
221+
Visitor::getFieldInputInfo<Conversion, T>(field_name);
238222
}
239223
}
240224
}
@@ -268,7 +252,7 @@ void Visitor::visitField(std::vector<ConfigT>& config, const std::string& field_
268252
}
269253

270254
if (visitor.mode == Visitor::Mode::kSet) {
271-
const auto array_ns = visitor.name_space.empty() ? field_name : visitor.name_space + "/" + field_name;
255+
const auto array_ns = joinNamespace(visitor.name_space, field_name);
272256
const auto subnode = lookupNamespace(visitor.data.data, array_ns);
273257
if (!subnode) {
274258
return; // don't override the field if not present
@@ -346,7 +330,7 @@ void Visitor::visitField(OrderedMap<K, ConfigT>& config, const std::string& fiel
346330
}
347331

348332
if (visitor.mode == Visitor::Mode::kSet) {
349-
const auto map_ns = visitor.name_space.empty() ? field_name : visitor.name_space + "/" + field_name;
333+
const auto map_ns = joinNamespace(visitor.name_space, field_name);
350334
const auto subnode = lookupNamespace(visitor.data.data, map_ns);
351335
if (!subnode) {
352336
return; // don't override the field if not present
@@ -470,14 +454,41 @@ void Visitor::getDefaultValues(const ConfigT& config, MetaData& data) {
470454
}
471455
}
472456

473-
template <typename Conversion, typename std::enable_if<!hasFieldInputInfo<Conversion>(), bool>::type>
474-
FieldInputInfo::Ptr Visitor::getFieldInputInfo() {
475-
return nullptr;
457+
// intentional no-op
458+
template <typename Conversion,
459+
typename ConfigT,
460+
typename std::enable_if<!hasFieldInputInfo<Conversion>() || isConfig<ConfigT>(), bool>::type>
461+
void Visitor::getFieldInputInfo(const std::string& field_name) {
462+
static_assert(!isConfig<ConfigT>() || !hasFieldInputInfo<Conversion>(),
463+
"Config types (with declare_config) cannot have field input information!");
464+
465+
if (isConfig<ConfigT>()) {
466+
return; // don't touch field info fields
467+
}
468+
469+
auto& visitor = Visitor::instance();
470+
if (visitor.data.field_infos.empty()) {
471+
visitor.data.errors.emplace_back(
472+
new Warning(field_name, "Invalid parsing state! Field info should already exist!"));
473+
} else {
474+
visitor.data.field_infos.back().input_info.reset(); // clear field input info for underlying intermediate type
475+
}
476476
}
477477

478-
template <typename Conversion, typename std::enable_if<hasFieldInputInfo<Conversion>(), bool>::type>
479-
FieldInputInfo::Ptr Visitor::getFieldInputInfo() {
480-
return Conversion::getFieldInputInfo();
478+
template <typename Conversion,
479+
typename ConfigT,
480+
typename std::enable_if<hasFieldInputInfo<Conversion>() && !isConfig<ConfigT>(), bool>::type>
481+
void Visitor::getFieldInputInfo(const std::string& field_name) {
482+
auto input_info = Conversion::getFieldInputInfo();
483+
484+
auto& visitor = Visitor::instance();
485+
if (visitor.data.field_infos.empty()) {
486+
visitor.data.errors.emplace_back(
487+
new Warning(field_name, "Invalid parsing state! Field info should already exist!"));
488+
} else {
489+
auto& info = visitor.data.field_infos.back();
490+
info.input_info = FieldInputInfo::merge(input_info, info.input_info);
491+
}
481492
}
482493

483494
} // namespace config::internal

config_utilities/test/tests/conversions.cpp

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,47 @@ struct TestConversionStruct {
8383
int test = 0;
8484
};
8585

86+
struct IntermediateConfigConversionStruct {
87+
struct Input {
88+
int a = 4;
89+
int b = 5;
90+
int value() const { return a + b; }
91+
};
92+
93+
struct Resolved {
94+
std::vector<int> inputs;
95+
};
96+
97+
struct Conversion {
98+
static std::vector<Input> toIntermediate(const std::vector<int>& value, std::string& /* error */) {
99+
std::vector<Input> to_return;
100+
for (const auto total : value) {
101+
to_return.push_back(Input{total, 0});
102+
}
103+
104+
return to_return;
105+
}
106+
107+
static void fromIntermediate(const std::vector<Input>& intermediate,
108+
std::vector<int>& value,
109+
std::string& /*error*/) {
110+
value.clear();
111+
for (const auto& input : intermediate) {
112+
value.push_back(input.value());
113+
}
114+
}
115+
};
116+
};
117+
118+
void declare_config(IntermediateConfigConversionStruct::Input& config) {
119+
field(config.a, "a");
120+
field(config.b, "b");
121+
}
122+
123+
void declare_config(IntermediateConfigConversionStruct::Resolved& config) {
124+
field<IntermediateConfigConversionStruct::Conversion>(config.inputs, "inputs");
125+
}
126+
86127
void declare_config(ConversionStruct& conf) {
87128
field<ThreadNumConversion>(conf.num_threads, "num_threads");
88129
field<CharConversion>(conf.some_character, "some_character");
@@ -206,4 +247,13 @@ TEST(Conversions, FieldInputInfo) {
206247
EXPECT_FALSE(data.field_infos[1].input_info);
207248
}
208249

250+
TEST(Conversions, ConversionDeclareConfigDispatch) {
251+
const std::string yaml_string = "inputs: [{a: 0, b: 1}, {a: 1, b: 1}, {a: 1, b: 2}, {a: 0, b: 4}]";
252+
const auto node = YAML::Load(yaml_string);
253+
254+
std::vector<int> expected{1, 2, 3, 4};
255+
const auto result = fromYaml<IntermediateConfigConversionStruct::Resolved>(node);
256+
EXPECT_EQ(result.inputs, expected);
257+
}
258+
209259
} // namespace config::test

0 commit comments

Comments
 (0)