I want to initialize many different objects of some type (structure) with different values for their fields and I want to use names for initialization to make code readable and robust.
Initializer lists for structures have two killing disadvantages:
Here is the task I am trying to solve. I have configuration for application which in this particular case I don’t want to put in external file and want to have compiled-in. This is very close to How to define C++ struct for configuration?, but I am not ready to put frequently changing parameters to a header file and cause whole application recompilation with every change of the value in the params source code (specifics or R&D project, many things to tune frequently). So, I put actual values in cpp file.
Let’s consider the code (please don’t stuck on the specific example, the UI colors here just for simplicity of the example): params.h
struct RGB { unsigned char r=0, g=0, b=0; };
struct UIColors {
RGB background;
RGB font;
};
namespace params {
extern bool ui_bright_mode;
extern UIColors night_mode;
extern UIColors bright_mode;
}
params.cpp
namespace params {
bool ui_bright_mode = 1;
UIColors night_mode = {
/*background*/ {1, 1, 1},
/*font*/ {0, 0, 200}
};
UIColors bright_mode = {
/*background*/ { 255, 255, 255 },
/*font*/ { 50, 50, 50 }
};
}
application.cpp
#include <iostream>
UIColors& ui_colors() {
return params::ui_bright_mode ? params::bright_mode : params::night_mode;
}
int main()
{
std::cout << params::ui_bright_mode << '\n';
std::cout
<< (int) ui_colors().background.r << ", "
<< (int) ui_colors().background.g << ", "
<< (int) ui_colors().background.b << '\n';
params::ui_bright_mode = 0;
std::cout << params::ui_bright_mode << '\n';
std::cout
<< (int)ui_colors().background.r << ", "
<< (int)ui_colors().background.g << ", "
<< (int)ui_colors().background.b << '\n';
}
Full Demo (in one file, since it is hard to make many there).
The question is, is there any reasonable and robust approach which could allow me to keep the values in C++ file (params.cpp above) and at the same time make initialization in a way I have for ui_bright_mode
variable, by fields’ names?
I would be happy to have something like:
UIColors night_mode = {
background = {1, 1, 1},
font = {0, 0, 200}
};
at the same time
RGB background = {1, 1, 1};
RGB font = {0, 0, 200};
UIColors night_mode = {
background,
font
};
would be the case, but two verbose and still won't defend from fields order changes and mess or similar fields; still hard to find a proper place for this "temporary variables" and still not always applicable.
I can’t use nested namespaces because I want to have a luxury to copy structures or select one of them as in the example above with the ui_colors()
function. This would be impossible with the namespaces.
I would be happy to avoid heavy macro-based solution, preferring something language-based or std-based. And, of course, I want to avoid using maps to store values because of performance and simplicity considerations.
The best way I know to initialize structures using field names is using designated initializers like in Gene's comment. It's available in C++20, and fitting to your specifications of keeping initialization readable and minimally verbose. Leveraging on designated initializers allows you to explicitly name fields during initialization.
Here is an example muching to yours:
//params.cpp
namespace params
{
bool ui_bright_mode = true;
UIColors night_mode =
{
.background = {1, 1, 1},
.font = {0, 0, 200}
};
UIColors bright_mode =
{
.background = {255, 255, 255},
.font = {50, 50, 50}
};
}
Here is a full demo . Each field is explicitly named during initialization with the benefit of no overhead. Moreover, this method is order independent, so changes to field order in UIColors won't break the code. It's Supported in GCC since version 10 and in Clang since version 9. And again I'll say, make sure you're using c++20. Good luck with your project!