I have a class with variadic template parameters and I am trying to write a member function that creates a container, that contains the variadic parameters. My approach is to create a tuple from the parameters in order to unfold the elements of the tuple and call a function for reach element that adds them to my container class, see the following code (sorry for quite a long example, it's a bit intricate and I don't know to to boil it down further):
#include <string>
#include <tuple>
#include <vector>
#include <map>
#include <memory>
#include <iostream>
// these are a couple of mock-up classes, basically we want to save entities of arbitrary parameters
// base class for a type erased parameter
struct TypeErasedParameter
{
private:
const std::string m_strId;
protected:
TypeErasedParameter(std::string id) : m_strId(id) {}
public:
virtual ~TypeErasedParameter() {}
const std::string & GetId()
{
return m_strId;
}
template <typename T>
T& GetValue();
};
// implementation of the type erased parameter, storing the parameter T in parameterValue
template <typename T>
struct Parameter : public TypeErasedParameter
{
T parameterValue;
Parameter(const std::string id, T value) : TypeErasedParameter(id), parameterValue(value) {}
};
template <typename T>
T& TypeErasedParameter::GetValue()
{
return dynamic_cast<Parameter<T>*>(this)->parameterValue;
}
// ParameterPack is basically a container that stores a bunch of TypeErasedParameters
struct ParameterPack
{
std::map<std::string, std::unique_ptr<TypeErasedParameter>> m_mapParamsById;
template<typename ParameterType>
ParameterType& GetParameter(const std::string& strParamName)
{
return m_mapParamsById.at(strParamName)->GetValue<ParameterType>();
}
template<typename ParameterType>
bool AddParameter(const std::string& strParamName, ParameterType& parameter)
{
m_mapParamsById.insert(std::make_pair(strParamName, parameter));
return true;
}
};
// actual class of interest, the creator and unpacker of parameter packs
template <typename ...Args>
struct ParameterUnpacker
{
public:
std::vector<std::string> m_vecIds;
ParameterUnpacker(std::vector<std::string> vecIds) : m_vecIds(vecIds) {}
const std::vector<std::string>& GetIds()
{
return m_vecIds;
}
template <typename Tuple, std::size_t... Is>
std::unique_ptr<ParameterPack> CreateParameterPackHelper(Tuple&& tuple, std::index_sequence<Is...>)
{
std::unique_ptr<ParameterPack> pParamPack = std::unique_ptr<ParameterPack>(new ParameterPack());
const auto& vecIds = GetIds();
// TODO: This line I'm not sure about
// my plan is to unfold the tuple passed to this function and call
// AddParameter for each element
if (!(pParamPack->AddParameter(vecIds.at(Is), std::get<Is>(tuple)) && ...))
{
return nullptr;
}
return pParamPack;
}
// with this function we want to be able to pass the parameters to our Unpacker so that it creates a
// parameterPack with the parameters in it for us
template <typename... InputArgs>
std::unique_ptr<ParameterPack> CreateParameterPack(InputArgs&... args)
{
// TODO: This line I'm also not sure about
// my way of thought is to create a tuple of the the variadic template and
// have CreateParameterPackHelper handle the rest
return CreateParameterPackHelper(std::tuple<InputArgs...>{}, std::index_sequence_for<InputArgs ...>{});
}
};
int main(int argc, char* argv[]) {
// create a new pack using ParameterUnpacker.CreateParameterPack()
ParameterUnpacker<double, int> unpacker({ "doubleParam" , "intParam" });
const double double_param = 1.123;
const int int_param= 12;
auto newParamPack = unpacker.CreateParameterPack(double_param , int_param);
return 0;
}
Here is the code above as an example with the err message: http://coliru.stacked-crooked.com/a/f315a81cb02f18e0
What I am ultimately not sure about is, how do I assure that the variadic template with which the instance of the ParameterUnpacker is created (i.e. template <typename ...Args>
), fits with the ones that I am passing to unpack.CreateParameterPack(firstParam, secondParam, ...)
(i.e. the template <typename... InputArgs>
)? How do know from which ones I am creating the tuple to pass on to CreateParameterPackHelper? And how do I add each parameter to the ParameterPack that unpacker.CreateParameterPack
is creating?
Thanks a lot in advance for you help!
Update: I was able to solve my problem, also resolving the ambiuous usage of two sets of variadic template parmeters (the ...Args and ...InputArgs), @Jarod42's hint was critial I believe. Here is an updated version of the functional code: http://coliru.stacked-crooked.com/a/20fe1fcb6715bf4b
There are some mistakes:
template <typename... InputArgs>
std::unique_ptr<ParameterPack> CreateParameterPack(InputArgs&... args)
{
return CreateParameterPackHelper(std::tuple<InputArgs...>{args...},
// ^^^^^^^
std::index_sequence_for<InputArgs ...>{});
}
You forget to use args
.
template <typename ParameterType>
bool AddParameter(const std::string& strParamName, const ParameterType& parameter)
{
m_mapParamsById[strParamName] = std::make_unique<Parameter<ParameterType>>(parameter));
return true;
}
You have to construct a Parameter<ParameterType>
here.
Your usage of TypeErasedParameter
is risky, as you will have UB with mismatching type.
std::any
seems more appropriate:
struct TypeErasedParameter
{
private:
const std::string m_strId;
std::any value;
public:
template <typename T>
TypeErasedParameter(std::string id, const T& value) : m_strId(id), value(value) {}
const std::string& GetId() { return m_strId; }
template <typename T>
T& GetValue() { return std::any_cast<T&>(value); }
};
struct ParameterPack
{
std::map<std::string, TypeErasedParameter> m_mapParamsById;
// TypeErasedParameter no longer to be pointer
template <typename ParameterType>
ParameterType& GetParameter(const std::string& strParamName)
{
return m_mapParamsById.at(strParamName).GetValue<ParameterType>();
}
template<typename T>
bool AddParameter(const std::string& strParamName, const T& parameter)
{
m_mapParamsById.emplace(strParamName, TypeErasedParameter(strParamName, parameter));
return true;
}
};