I have a templated static class with different specializations, like this:
template<typename Parameter >
class MyClass
{};
template<>
class MyClass<Parameter1>
{
public:
static constexpr Integer myarray[]={0};
static constexpr Integer myarraysize=1;
};
template<>
class MyClass<Parameter2>
{
public:
static constexpr Integer myarray[]={0,1};
static constexpr Integer myarraysize=2;
};
Now I would like to group somehow these informations in a new class
template<typename MyClass1, typename MyClasses... >
class MyGroupClass{
//do something...}
where I could give as variadic template parameters the different classes and then I could access the different static methods.
For example, I would like to to something as MyGroupClass[n]::myarraysize
to access myarraysize
related to n-th MyClass
.
I imagine I could create a tuple (so having std::get<n>()
), but it is not very clear to me how to do this, since I have no constructors for such single static classes. After all, the classes are static.
Is it possible to achieve what I want? If so, could you please enlighten me? Thank you.
I would like to to something as MyGroupClass[n]:: myarraysize to access myarraysize related to n-th MyClass. I imagine I could create a tuple (so having std::get()),
It seems to me that you have to distinguish two cases.
(1) when the myarraysize
in the different classes are of different types you can create a std::tuple
of the different sizes an use std::get()
to extract the values.
By example
template <typename ... Ts>
struct MyGroupStruct
{
const std::tuple<decltype(Ts::myarraysize)...> tpl { Ts::myarraysize... };
template <std::size_t N>
auto get () const -> decltype(std::get<N>(tpl))
{ return std::get<N>(tpl); }
};
Starting from C++14 you can avoid the trailing return type for get()
and write simply
template <std::size_t N>
auto get () const
{ return std::get<N>(tpl); }
Observe that the MyGroupStruct::get()
receive the index (N
) as template parameter. So it needs a compile time known value. This is necessary because the type returned from the template method differ depending from the index so must be known compile time.
(2) when all myarraysize
in different classes are of the same type, you can also create a std::array
of that type; something as
template <typename ... Ts>
struct MyGroupStruct
{
using myType = typename std::tuple_element<0u,
std::tuple<decltype(Ts::myarraysize)...>>::type;
const std::array<myType, sizeof...(Ts)> arr {{ Ts::myarraysize... }};
myType & get (std::size_t n)
{ return arr[n]; }
};
Observe that, in this case, the return value for get()
is ever the same so it can receive a run-time index as (not template) parameter.
The following is a full compiling example for the different-types (tuple based) case
#include <tuple>
#include <string>
#include <iostream>
struct Par1 {};
struct Par2 {};
struct Par3 {};
struct Par4 {};
template <typename>
struct MyStruct;
template <>
struct MyStruct<Par1>
{ static constexpr int myarraysize {1}; };
constexpr int MyStruct<Par1>::myarraysize;
template <>
struct MyStruct<Par2>
{ static constexpr long myarraysize {2l}; };
constexpr long MyStruct<Par2>::myarraysize;
template <>
struct MyStruct<Par3>
{ static constexpr long long myarraysize {3ll}; };
constexpr long long MyStruct<Par3>::myarraysize;
template <>
struct MyStruct<Par4>
{ static const std::string myarraysize; };
const std::string MyStruct<Par4>::myarraysize {"four"};
template <typename ... Ts>
struct MyGroupStruct
{
const std::tuple<decltype(Ts::myarraysize)...> tpl { Ts::myarraysize... };
template <std::size_t N>
auto get () const -> decltype(std::get<N>(tpl))
{ return std::get<N>(tpl); }
};
int main ()
{
MyGroupStruct<MyStruct<Par1>, MyStruct<Par2>,
MyStruct<Par3>, MyStruct<Par4>> mgs;
std::cout << mgs.get<0>() << std::endl;
std::cout << mgs.get<1>() << std::endl;
std::cout << mgs.get<2>() << std::endl;
std::cout << mgs.get<3>() << std::endl;
static_assert( std::is_same<int const &,
decltype(mgs.get<0>())>::value, "!" );
static_assert( std::is_same<long const &,
decltype(mgs.get<1>())>::value, "!" );
static_assert( std::is_same<long long const &,
decltype(mgs.get<2>())>::value, "!" );
static_assert( std::is_same<std::string const &,
decltype(mgs.get<3>())>::value, "!" );
}
And now a full compiling example for the equal-types (array based) case
#include <tuple>
#include <array>
#include <string>
#include <iostream>
struct Par1 {};
struct Par2 {};
struct Par3 {};
struct Par4 {};
template <typename>
struct MyStruct;
template <>
struct MyStruct<Par1>
{ static constexpr int myarraysize {1}; };
constexpr int MyStruct<Par1>::myarraysize;
template <>
struct MyStruct<Par2>
{ static constexpr int myarraysize {2}; };
constexpr int MyStruct<Par2>::myarraysize;
template <>
struct MyStruct<Par3>
{ static constexpr int myarraysize {3}; };
constexpr int MyStruct<Par3>::myarraysize;
template <>
struct MyStruct<Par4>
{ static const int myarraysize {4}; };
const int MyStruct<Par4>::myarraysize;
template <typename ... Ts>
struct MyGroupStruct
{
using myType = typename std::tuple_element<0u,
std::tuple<decltype(Ts::myarraysize)...>>::type;
const std::array<myType, sizeof...(Ts)> arr {{ Ts::myarraysize... }};
myType & get (std::size_t n)
{ return arr[n]; }
};
int main ()
{
MyGroupStruct<MyStruct<Par1>, MyStruct<Par2>,
MyStruct<Par3>, MyStruct<Par4>> mgs;
std::cout << mgs.get(0) << std::endl;
std::cout << mgs.get(1) << std::endl;
std::cout << mgs.get(2) << std::endl;
std::cout << mgs.get(3) << std::endl;
static_assert( std::is_same<int const &,
decltype(mgs.get(0))>::value, "!" );
}
-- EDIT --
The OP asks
how to change the code if I want to access also myarray and not only myarraysize?
With a C-style array is a little more complicated because you can't initialize a tuple of C-style arrays with C-style arrays.
I propose you to use a tuple of references to C-style arrays.
So, given some MyClass
es with only myarray
(why add the size when you can deduce it?)
template <typename>
struct MyStruct;
template <>
struct MyStruct<Par1>
{ static constexpr int myarray[] {0}; };
constexpr int MyStruct<Par1>::myarray[];
// other MyStruct specializations ...
you can add a tuple of references (tuple, not std::array
, because int[1]
, int[2]
, int[3]
and int[4]
are all different types) and a std::array<std::size_t, sizeof...(Ts)>
for sizes.
I mean... you can write something as follows
template <typename ... Ts>
struct MyGroupStruct
{
std::tuple<decltype(Ts::myarray) & ...> const tpl { Ts::myarray... };
std::array<std::size_t, sizeof...(Ts)> const arr
{{ sizeof(Ts::myarray)/sizeof(Ts::myarray[0])... }};
template <std::size_t N>
auto getArr () const -> decltype(std::get<N>(tpl))
{ return std::get<N>(tpl); }
std::size_t getSize (std::size_t n) const
{ return arr[n]; }
};
what does this stand for "const -> decltype(std::get(tpl))"?
const
is unrelated to decltype()
.
const
, after the list of method parameters, say that the method can be used also by constant object because doesn't change member variables.
Regarding decltype()
look for "trailing return type" for more informations.
In short, for C++11, the idea is that in
auto foo () -> decltype(something)
{ return something; }
auto
say "look after -> for the return type" and decltype(something)
is "the type of something
"
You can also write
decltype(something) foo ()
{ return something; }
if something
is known before the list of function arguments, but the auto
/-> decltype(something)
form become useful when something
contains template parameters
By example
template <typename T1, typename T2>
auto sum (T1 const & t1, T2 const & t2) -> decltype(t1+t2)
{ return t1+t2; }
Starting from C++14 the "trailing return type" is less used because you can simply write
template <typename T1, typename T2>
auto sum (T1 const & t1, T2 const & t2)
{ return t1+t2; }
because auto
say to the compiler "deduce the return type from the return
expression" (so from t1+t2
in this case.
This avoid a lot of redundancies.
And can we use "auto" also in C++11?
auto
as return type? Without trailing return type?
Unfortunately is available only starting from C++14.
Follows another full example with myarray
and deduced sizes.
#include <tuple>
#include <array>
#include <string>
#include <iostream>
struct Par1 {};
struct Par2 {};
struct Par3 {};
struct Par4 {};
template <typename>
struct MyStruct;
template <>
struct MyStruct<Par1>
{ static constexpr int myarray[] {0}; };
constexpr int MyStruct<Par1>::myarray[];
template <>
struct MyStruct<Par2>
{ static constexpr int myarray[] {0, 1}; };
constexpr int MyStruct<Par2>::myarray[];
template <>
struct MyStruct<Par3>
{ static constexpr int myarray[] {0, 1, 2}; };
constexpr int MyStruct<Par3>::myarray[];
template <>
struct MyStruct<Par4>
{ static constexpr int myarray[] {0, 1, 2, 3}; };
constexpr int MyStruct<Par4>::myarray[];
template <typename ... Ts>
struct MyGroupStruct
{
std::tuple<decltype(Ts::myarray) & ...> const tpl { Ts::myarray... };
std::array<std::size_t, sizeof...(Ts)> const arr
{{ sizeof(Ts::myarray)/sizeof(Ts::myarray[0])... }};
template <std::size_t N>
auto getArr () const -> decltype(std::get<N>(tpl))
{ return std::get<N>(tpl); }
std::size_t getSize (std::size_t n) const
{ return arr[n]; }
};
int main ()
{
MyGroupStruct<MyStruct<Par1>, MyStruct<Par2>,
MyStruct<Par3>, MyStruct<Par4>> mgs;
std::cout << mgs.getSize(0) << std::endl;
std::cout << mgs.getSize(1) << std::endl;
std::cout << mgs.getSize(2) << std::endl;
std::cout << mgs.getSize(3) << std::endl;
static_assert( std::is_same<std::size_t,
decltype(mgs.getSize(0))>::value, "!" );
std::cout << mgs.getArr<0>()[0] << std::endl;
std::cout << mgs.getArr<1>()[1] << std::endl;
std::cout << mgs.getArr<2>()[2] << std::endl;
std::cout << mgs.getArr<3>()[3] << std::endl;
static_assert( std::is_same<int const (&)[1],
decltype(mgs.getArr<0>())>::value, "!" );
static_assert( std::is_same<int const (&)[2],
decltype(mgs.getArr<1>())>::value, "!" );
static_assert( std::is_same<int const (&)[3],
decltype(mgs.getArr<2>())>::value, "!" );
static_assert( std::is_same<int const (&)[4],
decltype(mgs.getArr<3>())>::value, "!" );
}