Overridable struct of params - c++

I'm trying to find the most optimal way of doing the following. I have a big hierarchical structure of heterogeneous parameters like this (just as an example)
struct ServiceParams {
struct FilterParams {
bool remove_odds;
bool remove_primes;
}
size_t length;
std::optional<float> threshold;
FilterParams filter_params;
}
The service which uses these parameters fills the values from a config file while starting, and then the service allows a user to provide their own parameters via request:
class Service {
ServiceParams default_params;
bool HandleRequest(Data data, std::optional<ServiceParams> user_params) {
return DoSomeStuff(data, user_params.value_or(default_params))
}
}
So the question is: what is the best way of allowing user to specify only some part of user_params and use other params stored in default_params.
What I did consider so far:
Dynamic structure like std::map<std::string, std::any>
I could use std::map<std::string, std::any> for parameters and just loop (recursively) through user provided key-value store and override all the parameters which were specified.
The main drawback is: hard to read and understand the code and static code analysers wouldn't show me the places where a certain parameter is used. And I need to provide getters for each of the parameters.
Make all the params to be std::optional
struct OptionalServiceParams {
struct OptionalFilterParams {
std::optional<bool> remove_odds;
std::optional<bool> remove_primes;
}
std::optional<size_t> length;
std::optional<std::optional<float>> threshold;
std::optional<OptionalFilterParams> filter_params;
}
Drawback: but I don't want the inside implementations be bothered by these std::optional-s. It's not their job to extract contained values all the time.
Use both: the current and with std::optional-s
I can create the above optional structure for user's requests and user the current params structure for the inside implementation.
Drawback: I need to override all the parameters manually one by one
auto params = default_params;
if (user_params.length.has_value())
params.length = user_params.length.value();
if (user_params.threshold.has_value())
params.threshold = user_params.threshold.value();
if (user_params.filter_params.has_value()) {
if (user_params.filter_params->remove_odds.has_value())
params.filter_params.remove_odds = user_params.filter_params->remove_odds.value();
if (user_params.filter_params->remove_primes.has_value())
params.filter_params.remove_primes = user_params.filter_params->remove_primes.value();
}
It's easy to make a bug and it's hard to maintain and scale. And of course it's a lot of code duplication.
Make the above two structures as one but template
We could make it as (truncated, the question becomes too long)
template <template <typename ValueType>, typename...> typename Container = std::type_identity_t>
struct ServiceParams {
Container<size_t> length;
Container<std::optional<float>> threshold;
}
and use it like this
ServiceParams<> default_params;
ServiceParams<std::optional> user_params;
Now I can use default_params just as is (inside implementation) and there is no code duplication.
Drawback: it doesn't solve the problem with manual overriding all the params. And in contrast with the previous approach it's almost impossible to support designated initialisers here.
Resume
The other languages (e.g. python) provides reflection and I can treat a structure as a key-value store without a need of making getters. Plus static code analysers shows me the places where a certain parameter is used.
I'm 100% sure some classic approach exists but I failed to find it online.

How about something like this: Define tag types that allow overloading (when the type is known at compile time) or variants if it is not.
#include <variant>
#include <initializer_list>
namespace params {
struct filter_params_remove_odd{ bool value; };
struct filter_params_remove_primes{ bool value; };
struct length{ size_t value; };
} /* namespace params */
struct ServiceParams {
struct FilterParams {
bool remove_odds;
bool remove_primes;
};
using OverrideVariant = std::variant<
params::filter_params_remove_odd,
params::filter_params_remove_primes,
params::length>;
size_t length;
std::optional<float> threshold;
FilterParams filter_params;
void apply(params::filter_params_remove_odd param) noexcept
{ filter_params.remove_odds = param.value; }
void apply(params::filter_params_remove_primes param) noexcept
{ filter_params.remove_primes = param.value; }
void apply(params::length param) noexcept
{ length = param.value; }
void apply(const OverrideVariant& param) noexcept
{
std::visit([this]<class Param>(const Param& param) mutable {
this->apply(param); },
param);
}
void apply_all(std::initializer_list<OverrideVariant> params) noexcept
{
for(const OverrideVariant& param: params)
apply(param);
}
template<class... Param>
void apply_all(Param&&... params) noexcept
{ (apply(std::forward<Param>(params)), ...); }
};
bool HandleRequest(ServiceParams params,
std::initializer_list<ServiceParams::OverrideVariant> user_params)
{
params.apply_all(user_params);
return DoSomething(params);
}
template<class... Param>
bool HandleRequest(ServiceParams params, Param&&... user_params)
{
params.apply_all(std::forward<Param>(user_params)...);
return DoSomething(params);
}
void MyRequest(const ServiceParams& params)
{
// use initializer list overload
HandleRequest(params, {
params::filter_params_remove_odd{true},
params::length{12},
});
}
void MyRequest2(const ServiceParams& params)
{
// use parameter pack overload
HandleRequest(params,
params::filter_params_remove_odd{true},
params::length{12});
}
Alternatively, an enum class for the parameter and a variant or std::any for the value are also a good combination.
One downside of the variant approach is that you lose ABI compatibility whenever you add new parameters. This may or may not be an issue depending on your use case and coding style. In that case, an enum class + any pair per parameter would be a better option. Then of course you have to deal with mismatched types at runtime.

Related

Can static polymorphism (templates) be used despite type erasure?

Having returned relatively recently to C++ after decades of Java, I am currently struggling with a template-based approach to data conversion for instances where type erasure has been applied. Please bear with me, my nomenclature may still be off for C++-natives.
This is what I am trying to achieve:
Implement dynamic variables which are able to hold essentially any value type
Access the content of those variables using various other representations (string, ints, binary, ...)
Be able to hold variable instances in containers, independent of their value type
Convert between variable value and representation using conversion functions
Be able to introduce new representations just by providing new conversion functions
Constraints: use only C++-11 features if possible, no use of libraries like boost::any etc.
A rough sketch of this might look like this:
#include <iostream>
#include <vector>
void convert(const std::string &f, std::string &t) { t = f; }
void convert(const int &f, std::string &t) { t = std::to_string(f); }
void convert(const std::string &f, int &t) { t = std::stoi(f); }
void convert(const int &f, int &t) { t = f; }
struct Variable {
virtual void get(int &i) = 0;
virtual void get(std::string &s) = 0;
};
template <typename T> struct VariableImpl : Variable {
T value;
VariableImpl(const T &v) : value{v} {};
void get(int &i) { convert(value, i); };
void get(std::string &s) { convert(value, s); };
};
int main() {
VariableImpl<int> v1{42};
VariableImpl<std::string> v2{"1234"};
std::vector<Variable *> vars{&v1, &v2};
for (auto &v : vars) {
int i;
v->get(i);
std::string s;
v->get(s);
std::cout << "int representation: " << i <<
", string representation: " << s << std::endl;
}
return 0;
}
The code does what it is supposed to do, but obvoiusly I would like to get rid of Variable::get(int/std::string/...) and instead template them, because otherwise every new representation requires a definition and an implementation with the latter being exactly the same as all the others.
I've played with various approaches so far, like virtual templated, methods, applying the CRDT with intermediate type, various forms of wrappers, yet in all of them I get bitten by the erased value type of VariableImpl. On one hand, I think there might not be a solution, because after type erasure, the compiler cannot possibly know what templated getters and converter calls it must generate. On the other hand I think i might be missing something really essential here and there should be a solution despite the constraints mentioned above.
This is a classical double dispatch problem. The usual solution to this problem is to have some kind of dispatcher class with multiple implementations of the function you want to dispatch (get in your case). This is called the visitor pattern. The well-known drawback of it is the dependency cycle it creates (each class in the hierarchy depends on all other classes in the hierarchy). Thus there's a need to revisit it each time a new type is added. No amount of template wizardry eliminates it.
You don't have a specialised Visitor class, your Variable serves as a Visitor of itself, but this is a minor detail.
Since you don't like this solution, there is another one. It uses a registry of functions populated at run time and keyed on type identification of their arguments. This is sometimes called "Acyclic Visitor".
Here's a half-baked C++11-friendly implementation for your case.
#include <map>
#include <vector>
#include <typeinfo>
#include <typeindex>
#include <utility>
#include <functional>
#include <string>
#include <stdexcept>
struct Variable
{
virtual void convertValue(Variable& to) const = 0;
virtual ~Variable() {};
virtual std::type_index getTypeIdx() const = 0;
template <typename K> K get() const;
static std::map<std::pair<std::type_index, std::type_index>,
std::function<void(const Variable&, Variable&)>>
conversionMap;
template <typename T, typename K>
static void registerConversion(K (*fn)(const T&));
};
template <typename T>
struct VariableImpl : Variable
{
T value;
VariableImpl(const T &v) : value{v} {};
VariableImpl() : value{} {}; // this is needed for a declaration of
// `VariableImpl<K> below
// It can be avoided but it is
// a story for another day
void convertValue(Variable& to) const override
{
auto typeIdxFrom = getTypeIdx();
auto typeIdxTo = to.getTypeIdx();
if (typeIdxFrom == typeIdxTo) // no conversion needed
{
dynamic_cast<VariableImpl<T>&>(to).value = value;
}
else
{
auto fcnIter = conversionMap.find({getTypeIdx(), to.getTypeIdx()});
if (fcnIter != conversionMap.end())
{
fcnIter->second(*this, to);
}
else
throw std::logic_error("no conversion");
}
}
std::type_index getTypeIdx() const override
{
return std::type_index(typeid(T));
}
};
template <typename K> K Variable::get() const
{
VariableImpl<K> vk;
convertValue(vk);
return vk.value;
}
template <typename T, typename K>
void Variable::registerConversion(K (*fn)(const T&))
{
// add a mutex if you ever spread this over multiple threads
conversionMap[{std::type_index(typeid(T)), std::type_index(typeid(K))}] =
[fn](const Variable& from, Variable& to) {
dynamic_cast<VariableImpl<K>&>(to).value =
fn(dynamic_cast<const VariableImpl<T>&>(from).value);
};
}
Now of course you need to call registerConversion e.g. at the beginning of main and pass it each conversion function.
Variable::registerConversion(int_to_string);
Variable::registerConversion(string_to_int);
This is not ideal, but hardly anything is ever ideal.
Having said all that, I would recommend you revisit your design. Do you really need all these conversions? Why not pick one representation and stick with it?
Implement dynamic variables which are able to hold essentially any value type
Be able to hold variable instances in containers, independent of their value type
These two requirements are quite challenging on its own. The class templates don't really encourage inheritance, and you already did the right thing to hold what you asked for: introduced a common base class for the class template, which you can later refer to in order to store pointers of the said type in a collection.
Access the content of those variables using various other representations (string, ints, binary, ...)
Be able to introduce new representations just by providing new conversion functions
This is where it breaks. Function templates assume common implementation for different types, while inheritance assumes different implementation for the same types.
You goal is to introduce different implementation for different types, and in order to make your requirements viable you have to switch to one of those two options instead (or put up with a number of functions for each case which you have already introduced yourself)
Edit:
One of the strategies you may employ to enforce inheritance approach is generalisation of the arguments to the extent where they can be used interchangeably by the abstract interface. E.g. you may wrap the converting arguments inside of a union like this:
struct Variable {
struct converter_type {
enum { INT, STRING } type;
union {
int* m_int;
std::string* m_string;
};
};
virtual void get(converter_type& var) = 0;
virtual ~Variable() = default;
};
And then take whatever part of it inside of the implementation:
void get(converter_type& var) override {
switch (var.type) {
case converter_type::INT:
convert(value, var.m_int);
break;
case converter_type::STRING:
convert(value, var.m_string);
break;
}
}
To be honest I don't think this is a less verbose approach compared to just having a number of functions for each type combination, but i think you got the idea that you can just wrap your arguments somehow to cement the abstract class interface.
Implement std::any. It is similar to boost::any.
Create a conversion dispatcher based off typeids. Store your any alongside the conversion dispatcher.
"new conversion functions" have to be passed to the dispatcher.
When asked to convert to a type, pass that typeid to the dispatcher.
So we start with these 3 types:
using any = std::any; // implement this
using converter = std::function<any(any const&)>;
using convert_table = std::map<std::type_index, converter>;
using convert_lookup = convert_table(*)();
template<class T>
convert_table& lookup_convert_table() {
static convert_table t;
return t;
}
struct converter_any: any {
template<class T,
typename std::enable_if<
!std::is_same<typename std::decay<T>::type, converter_any>::value, bool
>::type = true
>
converter_any( T&& t ):
any(std::forward<T>(t)),
table(&lookup_convert_table<typename std::decay<T>::type>())
{}
converter_any(converter_any const&)=default;
converter_any(converter_any &&)=default;
converter_any& operator=(converter_any const&)=default;
converter_any& operator=(converter_any&&)=default;
~converter_any()=default;
converter_any()=default;
convert_table const* table = nullptr;
template<class U>
U convert_to() const {
if (!table)
throw 1; // make a better exception than int
auto it = table->find(typeid(U));
if (it == table->end())
throw 2; // make a better exception than int
any const& self = *this;
return any_cast<U>((it->second)(self));
}
};
template<class Dest, class Src>
bool add_converter_to_table( Dest(*f)(Src const&) ) {
lookup_convert_table<Src>()[typeid(Dest)] = [f](any const& s)->any {
Src src = std::any_cast<Src>(s);
auto r = f(src);
return r;
};
return true;
}
now your code looks like:
const bool bStringRegistered =
add_converter_to_table(+[](std::string const& f)->std::string{ return f; })
&& add_converter_to_table(+[](std::string const& f)->int{ return std::stoi(f); });
const bool bIntRegistered =
add_converter_to_table(+[](int const& i)->int{ return i; })
&& add_converter_to_table(+[](int const& i)->std::string{ return std::to_string(i); });
int main() {
converter_any v1{42};
converter_any v2{std::string("1234")};
std::vector<converter_any> vars{v1, v2}; // copies!
for (auto &v : vars) {
int i = v.convert_to<int>();
std::string s = v.convert_to<std::string>();
std::cout << "int representation: " << i <<
", string representation: " << s << std::endl;
}
}
live example.
...
Ok, what did I do?
I used any to be a smart void* that can store anything. Rewriting this is a bad idea, use someone else's implementation.
Then, I augmented it with a manually written virtual function table. Which table I add is determined by the constructor of my converter_any; here, I know the type stored, so I can store the right table.
Typically when using this technique, I'd know what functions are in there. For your implementation we do not; so the table is a map from the type id of the destination, to a conversion function.
The conversion function takes anys and returns anys -- again, don't repeat this work. And now it has a fixed signature.
To add support for a type, you independently register conversion functions. Here, my conversion function registration helper deduces the from type (to determine which table to register it in) and the destination type (to determine which entry in the table), and then automatically writes the any boxing/unboxing code for you.
...
At a higher level, what I'm doing is writing my own type erasure and object model. C++ has enough power that you can write your own object models, and when you want features that the default object model doesn't solve, well, roll a new object model.
Second, I'm using value types. A Java programmer isn't used to value types having polymorphic behavior, but much of C++ works much better if you write your code using value types.
So my converter_any is a polymorphic value type. You can store copies of them in vectors etc, and it just works.

Can I associate one class with another from a template (using C++17 variant)?

I have some code that accepts one type of object and creates another type of object based on the type of the first. (There is a 1->1 relationship between the types.) I originally used a hash table (unordered_map<>) with a key based on the type of the first object to associate a creation function for the second object. But as I am learning more about the C++ features introduced since the last time I was full-time C++, I discovered std::variant<>.
I have successfully converted the implementation to use this C++17 feature. However, there is one remaining piece that is still a bit cumbersome. The design makes a call to a static member function of the second class to validate the contents of the first object, before instantiating an object of the second class. To handle this right now, I'm using a visitor structure with function operators overloaded for each input type.
What I'm wondering is if there is some way to use a template for the association, rather than the copied code with only the types different?
I've tried looking at the way std::variant<> works, and I see where the index of the type can be obtained with .index(). I can see how to instantiate an object based on an index, which I might use if I created a second std::variant<> with the object types. But, as you can see, I don't want to instantiate the object until the parameters have been validated. The function that does that is static, and I don't see a way to associate the parms type with the object type in a way that lets me make the static call.
(I also realize that these two visitor structures can be combined in the code below, but in the real code, the creation is longer and more complicated, and I would rather not have copies of it in each overload.)
struct Type1Parms {};
struct Type2Parms {};
struct Type3Parms {};
...
struct TypeBase {};
struct Type1 : public TypeBase
{
static bool ValidateParms(const Type1Parms&);
Type1(const Type1Parms&);
};
struct Type2 : public TypeBase
{
static bool ValidateParms(const Type2Parms&);
Type2(const Type2Parms&);
};
struct Type3 : public TypeBase
{
static bool ValidateParms(const Type3Parms&);
Type3(const Type3Parms&);
};
...
struct ValidateParmsVisitor
{
bool operator()(const Type1Parms& parms)
{
return Type1::ValidateParms(parms);
}
bool operator()(const Type2Parms& parms)
{
return Type2::ValidateParms(parms);
}
bool operator()(const Type3Parms& parms)
{
return Type3::ValidateParms(parms);
}
...
};
using TypeParms = std::variant<Type1Parms, Type2Parms, Type3Parms, ...>;
struct CreateObjectVisitor
{
std::unique_ptr<TypeBase> operator()(const Type1Parms& parms)
{
return std::make_unique<Type1>(parms);
}
std::unique_ptr<TypeBase> operator()(const Type2Parms& parms)
{
return std::make_unique<Type2>(parms);
}
std::unique_ptr<TypeBase> operator()(const Type3Parms& parms)
{
return std::make_unique<Type3>(parms);
}
...
};
template<typename TParms>
std::unique_ptr<TypeBase> CreateType(const TParms& parms)
{
unique_ptr<TypeBase> obj;
if (visit(ValidateParmsVisitor{}, parms))
obj = visit(CreateObjectVisitor{}, parms);
return std::move(obj);
}
Is there a way to make this association, especially as a type that can be used with a static member function call?
EDIT: I should explain that this is part of a much larger project, with a number of other design criteria that shape its design.
For example, this is for a client interface, where the API is meant to be as simple as can be expressed. The client only has visibility (via header) to the parms structures and a function that takes the parms & returns an object that contains the objects mentioned above. The original design did indeed have a base structure for the parms, which obviously had to be in the public header. However, this meant that a client could inherit from the base class themselves and pass this into the object creation function, or inherit from the acceptable structures. To avoid segfaults, this necessitated adding runtime checks to be sure the types were acceptable, which was mostly handled by the hash design--although it wasn't quite that simple. When I removed the hash design, I also lost this method of type validation, but I recognized that this would be replaced by a compile time check with the variant<>, handling custom structures (no base to check now). I also learned about the C++ version of the final keyword which handled the inheritance issue.
Additionally, while the code above does not show it, the parms structures contain multiple members and the ValidateParms() functions actually attempt to validate whether the values and combinations are valid.
You can create traits for the association:
template <typename T> struct from_param;
template <> struct from_param<Type1Parms> { using type = Type1; };
template <> struct from_param<Type2Parms> { using type = Type2; };
template <> struct from_param<Type3Parms> { using type = Type3; };
Then, you might do
using TypeParms = std::variant<Type1Parms, Type2Parms, Type3Parms>;
std::unique_ptr<TypeBase> CreateType(const TypeParms& parms)
{
if (std::visit([](const auto& param){
return from_param<std::decay_t<decltype(param)>>::type::ValidateParms(parms);
}, parms))
{
return std::visit([](const auto& param) -> std::unique_ptr<TypeBase> {
return std::make_unique<typename from_param<std::decay_t<decltype(param)>>::type>(parms);
}, parms);
}
return nullptr;
}
Demo
or without variant, if you call with correct type:
template <typename T>
auto CreateType(const T& parms)
{
if (from_param<T>::type::ValidateParms(parms))
{
return std::make_unique<typename from_param<T>::type>(parms);
}
return nullptr;
}
There is a very simple method, a set of overloaded functions:
unique_ptr<TypeBase> CreateType(Type1Params const& params)
{
return make_unique<Type1>(params);
}
unique_ptr<TypeBase> CreateType(Type2Params const& params)
{
return make_unique<Type2>(params);
}
unique_ptr<TypeBase> CreateType(Type3Params const& params)
{
return make_unique<Type3>(params);
}
Notes:
You can add another overload to catch other parameters and then return null, but I think a compile-time error would be preferable.
You could also use a template function and specializations, but there's probably little typing to safe that way.

How to avoid mistakes in functions with same type arguments

How can I avoid mistakes with passing parameters of same type to function?
Let's consider function reading some binary data:
std::vector<uint8_t> read(size_t offset, size_t amount);
It's so easy to mistake offset with amount(I did similar it many times).
I see solution to this:
struct Offset
{
explicit Offset(size_t value) : value{value}{}
size_t value;
};
struct Amount
{
explicit Amount(size_t value) : value{value}{}
size_t value;
};
std::vector<uint8_t> read(Offset offset, Amount amount);
Is there a better solution to avoid mistakes like that?
There are two approaches I can think of.
Tagged Types
This is essentially what you are suggesting in your question, but I would implement it generically.
template <typename Tag, typename T>
struct Tagged
{
explicit Tagged(const T& value) : value{value} { }
T value;
};
template <typename Tag, typename T>
Tagged<Tag, T> tag(const T& value)
{
return Tagged<Tag, T>{value};
}
struct OffsetTag
{ };
struct AmountTag
{ };
using Offset = Tagged<OffsetTag, std::size_t>;
using Amount = Tagged<AmountTag, std::size_t>;
std::vector<uint8_t> read(Offset offset, Amount amount);
This allows you to expand the same concept to other underlying data types.
Named Parameter Idiom
The Named Parameter Idiom is somewhat similar to the Options approach in #PaulBelanger's answer, but it can be used in place and doesn't allow the user to take the the curly-brace shortcut that brings you back to the same problem you had before. However, it will default-initialize all your parameters, so while you are protected from mixing up parameters, it can't force you to provide explicit values for all of them. For your example:
class ReadParams
{
public:
ReadParams() : m_offset{0}, m_amount{128}
{ }
ReadParams& offset(std::size_t offset)
{
m_offset = offset;
return *this;
}
// Could get rid of this getter if you can make the users
// of this class friends.
std::size_t offset() const { return m_offset; }
ReadParams& amount(std::size_t amount)
{
m_amount = amount;
return *this;
}
// Could get rid of this getter if you can make the users
// of this class friends.
std::size_t amount() const { return m_amount; }
private:
std::size_t m_offset;
std::size_t m_amount;
};
std::vector<uint8_t> read(const ReadParams& params);
int main()
{
read(ReadParams{}.offset(42).amount(2048)); // clear parameter names
// read(ReadParams{42, 2048}); // won't compile
read(ReadParams{}.offset(42)); // also possible, amount uses default value
}
You could implement the members of ReadParams as std::optionals and throw a runtime error if an uninitialized member is access; but you can no longer enforce at compile time that the user actually provides all parameters.
Another thing that you can do is to pass parameters in a structure. This also allows you to set sensible defaults for the values. This is especially useful when a constructor takes a large number of arguments. For example:
class FooService
{
public:
// parameters struct.
struct Options
{
ip::address listen_address = ip::address::any();
uint16_t port = 1337;
bool allow_insecure_requests = false;
std::string base_directory = "/var/foo/"
};
//constructor takes the options struct to pass values.
explicit FooService(FooServiceOptions options);
// ...
};
Which is then used like:
FooService::Options o;
o.port = 1338;
//all other values keep their defaults.
auto service = make_unique<FooService>(o);

Hash specialization for table of function pointer

Updates in bold
I am writing a hash function for a table of function pointers with the limitation that the structure of the function pointers and function table cannot be modified (i.e. they have been published to third-parties). Based on Can std::hash be used to hash function pointers?, std::hash can be used for function pointers. Adopting that, it yields the following solution.
The tedious part about this solution is that every time we add new APIs to FuncPointers struct, we'd have to modify the hash specialization to add the corresponding change (i.e. hashFunc(hashedValue, pFuncs->func3) ).
I am wondering if there's a better way to implement this hashing of function pointers so continuous modification to the hash specialization can be avoided?
typedef void (*func_type1) (int);
typedef void (*func_type2) (double);
typedef struct FuncPointers
{
func_type1 func1;
func_type2 func2;
...
} FuncPointers;
template <typename T> void hashFunc (size_t & HashedValue, T funcPointer)
{
std::hash<T> hash;
HashedValue ^= hash(funcPointer); // the XOR operator is randomly picked
}
namespace std
{
template<> struct hash<FuncPointers>
{
size_t operator()(FuncPointers *pFuncs)
{
size_t hashedValue = 0;
hashFunc(hashedValue, pFuncs->func1);
hashFunc(hashedValue, pFuncs->func2);
...
return hashedValue;
}
};
}
Start with this: https://stackoverflow.com/a/7115547/1774667
It provides a hash_tuple::hash<Tuple> that is a valid decent quality hasher (with combining and recursion support!) for a std::tuple.
Next, change FuncPointers as follows:
struct FuncPointers:std::tuple<func_type1, func_type2 /*, ...*/> {
// optional:
func_type1 func1() const { return std::get<0>(*this); }
func_type1& func1() { return std::get<0>(*this); }
//...
};
namespace std {
template<>
struct hash<FuncPointers> {
template<typename... Ts>
std::size_t operator()( std::tuple<Ts...> const& funcs ) const {
return hash_tuple::hash<std::tuple<Ts...>>{}(funcs);
}
};
}
which redirects your std::hash<FuncPointers> to invoke hash_tuple::hash<std::tuple<...>> on the parent of FuncPointers. If you do not want to inherit from std::tuple, changing it to a has-a instead of an is-a relationship should be easy.
The optional func() accessors give you closer to the old interface (just requires a () added), but also adds boilerplate.
An alternative would be:
template<unsigned N>
auto func() const->decltype( std::get<N>(*this) ){ return std::get<N>(*this); }
template<unsigned N>
auto& func()->decltype( std::get<N>(*this) ){ return std::get<N>(*this); }
which changes funcPointers.func1 to funcPointers.func<1>(), but gets rid of tonnes of boilerplate when you add a new func, and stays pretty similar to the old interface of funcPointers.
If there is not much code that is using the old interface, using std::get<N>() makes some sense.
If your names are more descriptive than func1 and you only used that for the example, an enumeration of the function names can be used with std::get or func<X> above. If you go with func<X> you can even make it typesafe (force the use of the named functions).
You'd be better off making your FuncPointers a std::tuple<func_type1, func_type2>. Then see this answer on hashing.
BTW, typedef struct FuncPointers { } FuncPointers is a C-ism which has never been necessary in C++.

Macro alternative for C++ code generation

My settings module has some redundant code:
#include <QSettings>
class MySettings
{
public:
// param1
void setParam1(QString param1) { _settings.setValue("param1", param1); }
string param1() { return _settings.value("param1").toString(); }
// param2
void setParam2(int param2) { _settings.setValue("param2", param2); }
int param2() { _settings.value("param2").toInt(); }
// param3
void setParam3(int param3) { _settings.setValue("param3", param3); }
int param3() { _settings.value("param3").toInt(); }
private:
QSettings _settings;
}
I managed to reduce the amount of code to write by using a macro. Here is an example for the QString parameter type:
#define INTSETTING(setter, getter) \
void set##setter(QString getter) { settings.setValue(#getter, getter);} \
QString getter() {return settings.value(#getter).toString();}
Since I'm using C++, I know that macro usage is bad. I'm looking for a cleaner alternative.
I gave a Qt example (QString) but it is a more general question.
Edit:
The macros make the definition of the above class much simpler:
class MySettings
{
public:
STRINGSETTING(Param1, param1)
INTSETTING(Param2, param2)
INTSETTING(Param3, param3)
STRINGSETTING(DefaultTitle, defaultTitle)
INTSETTING(MaxDocCount, maxDocCount)
private:
QSettings _settings;
}
You can either answer this in a religious fashion, or you can go back to the old principle: if it makes your code more readable, do it.
There are a lot of people who answer this in a religious way, they just hate the preprocessor and everything that's to do with it, and ban its use from their code.
On the other hand, there are people who routinely define macros to do repetitive task, I have done so on several occasions, most frequently just defining a macro for the use within a single function (used much in the way you can define subfunctions in GNU-C).
I think, the way people think about it is quite similar to the way people think about the goto statement: Most deamonize its use, others say it has its positive uses and should not be viewed as evil in itself. You need to decide this for yourself.
Here is one way that does not use macros:
class MySettings
{
public:
template <size_t N>
void setParam(QString param) { _settings.setValue(names[N], param); }
template <size_t N, typename T>
T param() { return _settings.value(names[N]).toString(); }
private:
QSettings _settings;
const char* names[3] = { "param1", "param2", "param3" };
}
You change the syntax a little so say e.g. settings.setParam<1>("string") and settings.param<1, string>() but in any case names param1, param2 etc were not so informative.
The only inconvenience is that the caller needs to specify the return type of param() apart from the parameter number. To get rid of this, you can specify all parameter types within MySettings, like this:
class MySettings
{
using types = std::tuple<string, int, int>;
public:
template<size_t N>
void setParam(QString param) { _settings.setValue(names[N], param); }
template<size_t N>
typename std::tuple_element<N, types>::type
param() { return _settings.value(names[N]).toString(); }
private:
QSettings _settings;
const char* names[3] = { "param1", "param2", "param3" };
}
You could of course further generalize this class to be used as a base for other settings classes. Within the base, the only things that need to be customized are members types and names.
However, keep in mind that if parameter names are informative indeed unlike your example, e.g. setTitle, setColor etc. then most probably there is no way to avoid macros. In this case, I prefer a macro that generates an entire struct rather than a piece of code within another class, hence probably polluting its scope. So there could be a struct for each individual parameter, generated by a macro given the parameter name. The settings class would then inherit all those individual structs.
EDIT
I "forgot" generalizing toString() in param() (thanks #Joker_vD). One way is this:
template<size_t N>
typename std::tuple_element<N, types>::type
param() {
using T = typename std::tuple_element<N, types>::type;
return get_value(type<T>(), _settings.value(names[N]));
}
where get_value<T>() is a helper function that you need to define and overload for the types supported by QSettings, calling the appropriate conversion member function for each type, for instance
template<typename V>
string get_value(type<string>, const V& val) { return val.toString(); }
template<typename V>
int get_value(type<int>, const V& val) { return val.toInt(); }
and type is just a helper struct:
template<typename T>
struct type { };
If QSettings itself was designed with templates in mind, you wouldn't need this. But you probably wouldn't need a wrapper in the first place.
Hiding members is good. But when you let the user edit/see them, you should impose some constraints: in each setter there should be a check before assigning the element a new value (which might even make your application crash).
Otherwise, there is little difference if the data was public.