I'm writing a kind of key-value database. The specs of the item to write are stored in a template, let's say
template<int address, typename T>
class ItemInfo{};
i.e ItemInfo<0x100, unsigned>
means that some item which is of type unsigned, will be stored at the address 0x100.
Now I want a write a method that effectively does the store. It should receive both an ItemInfo
and a value, something like this
template<typename ItemInfo, typename T>
void store(T value);
Note that ItemInfo
should be a a template argument, not a function argument. In that method I need to know what the ItemInfo
address and template type T
are, so I can accomplish two things;
ItemsInfo
type.So the question is, how could I write such store
method?
Notes: I can use C++20. The provided answer by @joergbrech is fine, but I wonder if there's a simpler solution, specially not involving concepts.
As you mention that you have C++20 available, you could define a concept that checks if a type is a realization of your template E and require that the template parameter of your function satisifies this concept.
In addition, you need to be able to extract the template type of E
. If you can modify E
, you can add a typedef to E
like so:
#include <type_traits>
template<typename T0>
class E{
public:
using type = T0;
};
// SFINAE to check if a type is a template realization of E
namespace details {
template <typename T> struct is_E : std::false_type {};
template <typename T> struct is_E<E<T>> : std::true_type {};
}
template <typename T> constexpr bool is_E_v = details::is_E<T>::value;
// concept for E<T>
template<typename T> concept is_E = requires { is_E_v<T>; };
// function requires template parameter to be E
template<is_E E>
void f(typename E::type val){};
int main()
{
f<E<double>>(2.0);
return 0;
}
https://godbolt.org/z/9d3hc4WGG
If you cannot modify E
, you can add some meta-programming to extract the type:
// meta-programming to extract the template parameter of E
namespace details {
template <typename T> struct E_value;
template <typename T> struct E_value<E<T>> { using type = T; };
}
template <typename T> using E_value_type = typename details::E_value<T>::type;
// function requires template parameter to be E
template<is_E E>
void f(E_value_type<E> val){};
https://godbolt.org/z/GndM76M1n
Addendum 1
Here is a simplification, that does not require the definition of any additional meta-programming structs. You just need to provide an additional template parameter to f
:
#include <type_traits>
template<typename T0>
class E{};
template <typename EType, typename T>
concept is_E_with_type = requires { std::is_same_v<EType, E<T>>; };
// function requires template parameter to be E
template<typename EType, typename T>
requires is_E_with_type<EType, T>
void f(T val){};
int main()
{
f<E<double>>(2.0);
return 0;
}
https://godbolt.org/z/6Y8bd75Y3
Addendum 2
If you don't like concepts or want to support older C++ standards, you could use static_assert
:
#include <type_traits>
template<typename T0>
class E{};
// function requires template parameter to be E
template<typename EType, typename T>
void f(T val){
static_assert(std::is_same<EType, E<T>>::value, "template parameter passed in is not a template realization of E.");
};
int main()
{
f<E<double>>(2.0);
//f<bool>(1); // static_assert fails
return 0;
}