meta object using "static virtual" functions - c++

I developed some kind of meta object mechanism in a project, in order to associate type names and properties of any type to an object (see the result here). I have a dirty code working and I'm trying to make it cleaner. Given the following dummy structures:
struct A
{
using Self = A;
using Base = void;
static std::string get_type_name(){ return { "A" }; }
static std::vector<int> get_properties(){ return { 0 }; }
};
#define SELF(class_name)\
using Base = Self;\
using Self = class_name;
struct AA : A
{
SELF(AA)
static std::string get_type_name() { return { "AA" }; }
};
struct AAA : AA
{
SELF(AAA)
static std::string get_type_name(){ return { "AAA" }; }
static std::vector<int> get_properties(){ return { 2, 1 }; }
};
I ended up with this code to get the type names of an object across its hierarchy:
// when the type has no Base member:
template<
typename T,
typename std::enable_if<std::is_same<typename T::Base, void>::value>::type* = nullptr
>
typename std::vector<decltype(T::Self::get_type_name())> get_type_names()
{
return { T::Self::get_type_name() };
}
// when the type has a Base member:
template<
typename T,
typename std::enable_if<!std::is_same<typename T::Base, void>::value>::type* = nullptr
>
typename std::vector<decltype(T::Self::get_type_name())> get_type_names()
{
auto data = get_type_names<typename T::Base>();
data.insert(data.begin(), T::Self::get_type_name());
return data;
}
And something similar for the properties:
template<
typename T,
typename std::enable_if<std::is_same<typename T::Base, void>::value>::type* = nullptr
>
decltype(T::Self::get_properties()) get_properties()
{
return { T::Self::get_properties() };
}
template<
typename T,
typename std::enable_if<!std::is_same<typename T::Base, void>::value>::type* = nullptr
>
decltype(T::Self::get_properties()) get_properties()
{
auto data = get_properties<typename T::Base>();
auto self_data = T::Self::get_properties();
data.insert(data.begin(), self_data.begin(), self_data.end());
return data;
}
When testing the code with this snippet:
template<typename T>
void print()
{
std::cout << T::get_type_name() << std::endl << "\thas types:" << std::endl;
for(auto type_name : get_type_names<T>())
{
std::cout << "\t\t" << type_name << std::endl;
}
std::cout << "\thas properties:" << std::endl;
for(auto property : get_properties<T>())
{
std::cout << "\t\t" << property << std::endl;
}
}
int main()
{
print<A>();
print<AA>();
print<AAA>();
return 0;
}
I get the following output:
A
has types:
A
has properties:
0
AA
has types:
AA
A
has properties:
0
0
AAA
has types:
AAA
AA
A
has properties:
2
1
0
0
This first prototype works well for the names, but as soon as an object is declared without properties, the ones from its base are duplicated. Does somebody see a simple way to correct the problem?
Bonus question: implementations for the meta functions are very similar, does somebody have an hint on how I could factorize the code?
A complete live example is available here

Here's an incomplete solution that will put you on the right track. First, everything is dependent on walking the list of the bases. So let's factor that into its own metafunction:
template <class... > struct typelist { };
template <class T, class... Bases>
struct get_bases
: get_bases<typename T::Base, Bases..., T>
{ };
template <class... Bases>
struct get_bases<void, Bases...>
{
using type = typelist<Bases...>;
};
template <class T>
using get_bases_t = typename get_bases<T>::type;
So now, get_bases_t<A> is typelist<A> and get_bases_t<AAA> is typelist<AAA, AA, A>. Now, all of your other functions are just based on walking down that typelist and doing stuff to it. So let's add an easy way to do that:
template <class T> struct tag { using type = T; };
template <class... Ts>
auto for_each(typelist<Ts...>) {
return [](auto&& f){
using swallow = int[];
(void)swallow{0,
(void(f(tag<Ts>{})), 0)...
};
};
}
Now, getting all the properties is a matter of turning all the bases into their corresponding get_properties() functions, and then calling all the unique ones. You could probably pick out the unique functions at compile-time, but this works too:
template <class T>
std::vector<int> get_properties() {
using bases = get_bases_t<T>;
using F = std::vector<int>(*)();
F last = nullptr;
std::vector<int> props;
for_each(bases{})([&](auto t){
using B = typename decltype(t)::type;
F cur = B::get_properties;
if (cur != last) {
auto next = cur();
props.insert(props.end(), next.begin(), next.end());
last = cur;
}
});
return props;
}
This approach is very straightforward for getting your type names too:
template <class T>
std::vector<std::string> get_type_names()
{
using bases = get_bases_t<T>;
std::vector<std::string> names;
names.reserve(len_v<bases>); // TODO
for_each(bases{})([&](auto t){
names.push_back(decltype(t)::get_type_name());
});
return names;
}
Or, in this case, simply:
template <class... Bs>
std::vector<std::string> get_type_names_impl(typelist<Bs...>) {
return {Bs::get_type_name()...};
}
template <class T>
std::vector<std::string> get_type_names()
{
return get_type_names_impl(get_bases_t<T>);
}

Related

Using multiple templates with the same class in C++

I have the following code in C++ -
template <class T>
class TempClass {
T value;
public:
TempClass(T item)
{
value = item;
}
T getValue()
{
return value;
}
};
int main()
{
TempClass<string>* String =
new TempClass<string>("Rin>Sakura");
cout << "Output Values: " << String->getValue()
<< "\n";
class TempClass<int>* integer = new TempClass<int>(9);
cout << "Output Values: " << integer->getValue();
}
What I would like to do is use multiple templates with the above class TempClass. I know one way of doing this is by using
template <class T1, class T2>
, but if I do that then all instances of the class must have 2 template arguments. What I want to do is something more like :
if (flag)
//initialize an instance of TempClass with one template
TempClass<string> s("haha");
else
//initialize an instance of TempClass with 2 templates.
TempClass<string, int> s("haha", 5);
Is there a way to do this without using another new class?
You can use a variadic template and an std::tuple to hold values of distinct types. Minimal example:
template<class... Ts>
class TempClass {
using Tuple = std::tuple<Ts...>;
Tuple values;
public:
TempClass(Ts... items) : values{items...} {}
template<std::size_t index>
std::tuple_element_t<index, Tuple> getValue() const {
return std::get<index>(values);
}
};
int main() {
TempClass<int, std::string, double> tc1{0, "string", 20.19};
std::cout << tc1.getValue<2>(); // Output: 20.19
}
std::tuple_element_t is available only since C++14. In C+11 you should be more verbose: typename std::tuple_element<index, Tuple>::type.

Variadic argument subset

I am trying to obtain a subset of the variadic arguments of current class wrapper to instantiate a new one
Currently I have this:
// Reference: https://stackoverflow.com/questions/27941661/generating-one-class-member-per-variadic-template-argument
// Template specialization
template<typename T, typename... Next> class VariadicClass;
// Base case extension
template <typename T>
class VariadicClass<T> {
private:
T value_;
protected:
void SetField(T & value) {
value_ = value;
}
T & GetField() {
return value_;
}
};
// Inductive case
template <typename T, typename ... Next>
class VariadicClass : public VariadicClass<T>, public VariadicClass<Next...> {
public:
// Copy the values into the variadic class
template <typename F>
void Set(F f) {
this->VariadicClass<F>::SetField(f);
}
// Retrieve by reference
template <typename F>
F & Get() {
return this->VariadicClass<F>::GetField();
}
};
And what I want to achieve is something along the following:
[C]: A subset of Args...
VariadicClass<[C]> * Filter(VariadicClass<Args...> input) {
return new VariadicClass<[C]>(GetSubsetFrom(input, [C]));
}
VariadicClass<int, bool, char> class1;
VariadicClass<int, bool> * variadic = Filter(class1);
You can assume that each type is only once in the variadic class and that I will always ask for a subset of the current variadic types. I don't know if this is currently possible in C++ 11?
Thank you for your help.
It seems to me that you're trying to reinvent the wheel (where "wheel", in this case, is std::tuple).
Anyway, what you ask seems simple to me
template <typename ... As1, typename ... As2>
VariadicClass<As1...> * Filter(VariadicClass<As2...> in)
{
using unused = int[];
auto ret = new VariadicClass<As1...>();
(void)unused { 0, (ret->template Set<As1>(in.template Get<As1>()), 0)... };
return ret;
}
The problem I see is that the As1... types (the types of the returned VariadicClass) aren't deducible by the returned value, so you can't write
VariadicClass<int, bool> * variadic = Filter(class1);
You have to explicit the As1... types calling Filter(), so
VariadicClass<int, bool> * variadic = Filter<int, bool>(class1);
or, maybe better,
auto variadic = Filter<int, bool>(class1);
The following is a full compiling example
#include <iostream>
template <typename, typename...>
class VariadicClass;
template <typename T>
class VariadicClass<T>
{
private:
T value_;
protected:
void SetField (T & value)
{ value_ = value; }
T & GetField ()
{ return value_; }
};
template <typename T, typename ... Next>
class VariadicClass : public VariadicClass<T>, public VariadicClass<Next...>
{
public:
template <typename F>
void Set (F f)
{ this->VariadicClass<F>::SetField(f); }
template <typename F>
F & Get()
{ return this->VariadicClass<F>::GetField(); }
};
template <typename ... As1, typename ... As2>
VariadicClass<As1...> * Filter(VariadicClass<As2...> in)
{
using unused = int[];
auto ret = new VariadicClass<As1...>();
(void)unused { 0, (ret->template Set<As1>(in.template Get<As1>()), 0)... };
return ret;
}
int main()
{
VariadicClass<int, bool, char> c1;
c1.Set<int>(42);
c1.Set<bool>(true);
c1.Set<char>('Z');
auto pC2 = Filter<int, bool>(c1);
std::cout << pC2->Get<int>() << std::endl;
std::cout << pC2->Get<bool>() << std::endl;
delete pC2;
}
Off Topic Unrequested Suggestion: you're using C++11 so... try to avoid the direct use of pointer and try to use smart pointers (std::unique_ptr, std::shared_ptr, etc.) instead.
First of all I think you shouldn't write your own variadic class as we already have std::tuplein place.
I wonder that you sit on c++11because it is quite old. Even c++14is outdated but if you can switch, the solution is very simple:
template < typename DATA, typename FILTER, std::size_t... Is>
auto Subset_Impl( const DATA& data, FILTER& filter, std::index_sequence<Is...> )
{
filter = { std::get< typename std::remove_reference<decltype( std::get< Is >( filter ))>::type>( data )... };
}
template < typename DATA, typename FILTER, typename IDC = std::make_index_sequence<std::tuple_size<FILTER>::value >>
auto Subset( const DATA& data, FILTER& filter )
{
return Subset_Impl( data, filter, IDC{} );
}
int main()
{
std::tuple< int, float, std::string, char > data { 1, 2.2, "Hallo", 'c' };
std::tuple< float, char > filter;
Subset( data, filter );
std::cout << std::get<0>( filter ) << " " << std::get<1>( filter ) << std::endl;
}
If you really want sit on outdated standards, you can easily implement the missing parts from the standard library your self. One related question is answered here: get part of std::tuple
How the helper templates are defined can also be seen on: https://en.cppreference.com/w/cpp/utility/integer_sequence

C++ wrapper template: how to pass it as a template parameter?

Question to C++ template gurus:
I have created two template "policies" (not sure if this is the right term), which implement storage of some value types in a vector of either dumb or smart pointers:
#include <algorithm>
#include <iostream>
#include <memory>
#include <string>
#include <unordered_map>
#include <vector>
template <typename T>
class DumbPtrVec
{
std::vector<T*> m_vec;
public:
using handle = size_t;
~DumbPtrVec() {
std::for_each(begin(m_vec), end(m_vec), [](T* p){ delete p; });
}
handle AddElement(T* p) {
const handle index = m_vec.size();
m_vec.push_back(p);
return index;
}
T* GetElement(const handle& i) {
T* p = (i < m_vec.size())? m_vec[i] : nullptr;
return p;
}
};
template <typename T>
class SmartPtrVec
{
std::vector<std::shared_ptr<T>> m_vec;
public:
using handle = std::weak_ptr<T>;
handle AddElement(T* p) {
m_vec.emplace_back(p);
return m_vec.back(); // gets converted to weak_ptr
}
T* GetElement(const handle& i) {
T* p = (i.expired())? nullptr : i.lock().get();
return p;
}
};
template <typename T, template<typename> typename STORAGE>
class Storage
{
STORAGE<T> m_values;
public:
using handle = typename STORAGE<int>::handle;
handle AddValue(T* v) { return m_values.AddElement(v); }
T* GetValue(handle h) { return m_values.GetElement(h); }
};
int main()
{
constexpr int N = 13;
Storage<int, DumbPtrVec> d;
auto dh = d.AddValue(new int(N));
std::cout << *d.GetValue(dh) << " == " << N <<std::endl;
Storage<int, SmartPtrVec> s;
auto sh = s.AddValue(new int(N));
std::cout << *s.GetValue(sh) << " == " << N << std::endl;
return 0;
}
Everything works fine, so far.
Then I added a template wrapper, that replaces the element "handle" with a unique string and keeps a look-up table for converting strings back to the handles. If this class is explicitly derived from either DumbPtrVec or SmartPtrVec class, everything works, e.g. for SmartPtrVec:
template <typename T>
class StringHandleWrapper : SmartPtrVec<T>
{
using super = typename SmartPtrVec<T>;
using Str2HandleMap = std::unordered_map<std::string, typename super::handle>;
Str2HandleMap m_Name2HandleMap;
public:
using handle = std::string;
handle AddElement(T* p) {
typename super::handle elem = super::AddElement(p);
static int counter = 0;
std::string uuid = std::to_string(++counter);
m_Name2HandleMap[uuid] = elem;
return uuid;
}
T* GetElement(const handle& uuid) {
auto it = m_Name2HandleMap.find(uuid);
return (it != m_Name2HandleMap.end())? super::GetElement(it->second) : nullptr;
}
};
and successful invocation:
Storage<int, StringHandleWrapper> s;
std::string handle = s.AddValue(new int(N));
But I can't figure out how to add a second template parameter, STORAGE, to StringHandleWrapper, so that it could wrap any of DumbPtrVec or SmartPtrVec...
If I change StringHandleWrapper to:
template <typename T, template<typename> typename STORAGE>
class StringHandleWrapper : STORAGE<T>
{
using super = typename STORAGE<T>;
//... rest unchanged
then I can't figure out how to instantiate Storage class, as compiler complains about "too few template arguments":
Storage<int, StringHandleWrapper<SmartPtrVec>> s;
I hope I'm missing something simple...
Thank you for taking your time to look through my long question!
Create another level of template for partial argument application:
template <template <typename, template<typename> typename> class W,
template <typename> typename S>
struct Apply
{
template <typename T> using type = W<T, S>;
};
Then instantiate Storage like this:
Storage<int, Apply<StringHandleWrapper, SmartPtrVec>::type> s;
Just found the answer (it was indeed simple):
I needed to introduce two single-parameter templates
template<typename T> using StringDumbHandleWrapper = StringHandleWrapper<T, DumbPtrVec>;
template<typename T> using StringSmartHandleWrapper = StringHandleWrapper<T, SmartPtrVec>;
and use the new names in Storage instantiation, e.g.
Storage<int, StringDumbHandleWrapper> s;
So much for the long question... :)

In C++, how to return different generic types depending on argument in a class?

I have this code:
template<class T1, class T2>
class Pair
{
private:
T1 first;
T2 second;
public:
void SetFirst(T1 first)
{
this.first = first;
}
void SetSecond(T2 second)
{
this.second = second;
}
T1 GetFirst()
{
return first;
}
T2 GetSecond()
{
return second;
}
};
How could I implement two single methods SetValue() and GetValue(), instead of the four I have, that decides depending on parameters which generic type that should be used? For instance I'm thinking the GetValue() method could take an int parameter of either 1 or 2 and depending on the number, return either a variable of type T1 or T2. But I don't know the return type beforehand so is there anyway to solve this?
Not sure to understand what do you want and not exactly what you asked but...
I propose the use of a wrapper base class defined as follows
template <typename T>
class wrap
{
private:
T elem;
public:
void set (T const & t)
{ elem = t; }
T get () const
{ return elem; }
};
Now your class can be defined as
template <typename T1, typename T2>
struct Pair : wrap<T1>, wrap<T2>
{
template <typename T>
void set (T const & t)
{ wrap<T>::set(t); }
template <typename T>
T get () const
{ return wrap<T>::get(); }
};
or, if you can use C++11 and variadic templates and if you define a type traits getType to get the Nth type of a list,
template <std::size_t I, typename, typename ... Ts>
struct getType
{ using type = typename getType<I-1U, Ts...>::type; };
template <typename T, typename ... Ts>
struct getType<0U, T, Ts...>
{ using type = T; };
you can define Pair in a more flexible way as follows
template <typename ... Ts>
struct Pair : wrap<Ts>...
{
template <typename T>
void set (T const & t)
{ wrap<T>::set(t); }
template <std::size_t N, typename T>
void set (T const & t)
{ wrap<typename getType<N, Ts...>::type>::set(t); }
template <typename T>
T get () const
{ return wrap<T>::get(); }
template <std::size_t N>
typename getType<N, Ts...>::type get ()
{ return wrap<typename getType<N, Ts...>::type>::get(); }
};
Now the argument of set() can select the correct base class and the correct base element
Pair<int, long> p;
p.set(0); // set the int elem
p.set(1L); // set the long elem
otherwise, via index, you can write
p.set<0U>(3); // set the 1st (int) elem
p.set<1U>(4); // set the 2nd (long) elem
Unfortunately, the get() doesn't receive an argument, so the type have to be explicited (via type or via index)
p.get<int>(); // get the int elem value
p.get<long>(); // get the long elem value
p.get<0U>(); // get the 1st (int) elem value
p.get<1U>(); // get the 2nd (long) elem value
Obviously, this didn't work when T1 is equal to T2
The following is a (C++11) full working example
#include <iostream>
template <std::size_t I, typename, typename ... Ts>
struct getType
{ using type = typename getType<I-1U, Ts...>::type; };
template <typename T, typename ... Ts>
struct getType<0U, T, Ts...>
{ using type = T; };
template <typename T>
class wrap
{
private:
T elem;
public:
void set (T const & t)
{ elem = t; }
T get () const
{ return elem; }
};
template <typename ... Ts>
struct Pair : wrap<Ts>...
{
template <typename T>
void set (T const & t)
{ wrap<T>::set(t); }
template <std::size_t N, typename T>
void set (T const & t)
{ wrap<typename getType<N, Ts...>::type>::set(t); }
template <typename T>
T get () const
{ return wrap<T>::get(); }
template <std::size_t N>
typename getType<N, Ts...>::type get ()
{ return wrap<typename getType<N, Ts...>::type>::get(); }
};
int main()
{
//Pair<int, int> p; compilation error
Pair<int, long, long long> p;
p.set(0);
p.set(1L);
p.set(2LL);
std::cout << p.get<int>() << std::endl; // print 0
std::cout << p.get<long>() << std::endl; // print 1
std::cout << p.get<long long>() << std::endl; // print 2
p.set<0U>(3);
p.set<1U>(4);
p.set<2U>(5);
std::cout << p.get<0U>() << std::endl; // print 3
std::cout << p.get<1U>() << std::endl; // print 4
std::cout << p.get<2U>() << std::endl; // print 5
}
C++ is statically typed, so the argument given must be a template-argument instead a function-argument.
And while it will look like just one function each to the user, it's really two.
template <int i = 1> auto GetValue() -> std::enable_if_t<i == 1, T1> { return first; }
template <int i = 2> auto GetValue() -> std::enable_if_t<i == 2, T2> { return second; }
template <int i = 1> auto SetValue(T1 x) -> std::enable_if_t<i == 1> { first = x; }
template <int i = 2> auto SetValue(T2 x) -> std::enable_if_t<i == 2> { second = x; }
I use SFINAE on the return-type to remove the function from consideration unless the template-argument is right.
For this particular situation, you should definitely prefer std::pair or std::tuple.
You can simply overload SetValue() (provided T1 and T2 can be distinguished, if not you have a compile error):
void SetValue(T1 x)
{ first=x; }
void SetValue(T2 x)
{ second=x; }
Then, the compiler with find the best match for any call, i.e.
Pair<int,double> p;
p.SetValue(0); // sets p.first
p.SetValue(0.0); // sets p.second
With GetValue(), the information of which element you want to retrieve cannot be inferred from something like p.GetValue(), so you must provide it somehow. There are several options, such as
template<typename T>
std::enable_if_t<std::is_same<T,T1>,T>
GetValue() const
{ return first; }
template<typename T>
std::enable_if_t<std::is_same<T,T2>,T>
GetValue() const
{ return second; }
to be used like
auto a = p.GetValue<int>();
auto b = p.GetValue<double>();
but your initial version is good enough.

Generating one class member per variadic template argument

I have a template class where each template argument stands for one type of value the internal computation can handle. Templates (instead of function overloading) are needed because the values are passed as boost::any and their types are not clear before runtime.
To properly cast to the correct types, I would like to have a member list for each variadic argument type, something like this:
template<typename ...AcceptedTypes> // e.g. MyClass<T1, T2>
class MyClass {
std::vector<T1> m_argumentsOfType1;
std::vector<T2> m_argumentsOfType2; // ...
};
Or alternatively, I'd like to store the template argument types in a list, as to do some RTTI magic with it (?). But how to save them in a std::initializer_list member is also unclear to me.
Thanks for any help!
As you have already been hinted, the best way is to use a tuple:
template<typename ...AcceptedTypes> // e.g. MyClass<T1, T2>
class MyClass {
std::tuple<std::vector<AcceptedTypes>...> vectors;
};
This is the only way to multiply the "fields" because you cannot magically make it spell up the field names. Another important thing may be to get some named access to them. I guess that what you're trying to achieve is to have multiple vectors with unique types, so you can have the following facility to "search" for the correct vector by its value type:
template <class T1, class T2>
struct SameType
{
static const bool value = false;
};
template<class T>
struct SameType<T, T>
{
static const bool value = true;
};
template <typename... Types>
class MyClass
{
public:
typedef std::tuple<vector<Types>...> vtype;
vtype vectors;
template<int N, typename T>
struct VectorOfType: SameType<T,
typename std::tuple_element<N, vtype>::type::value_type>
{ };
template <int N, class T, class Tuple,
bool Match = false> // this =false is only for clarity
struct MatchingField
{
static vector<T>& get(Tuple& tp)
{
// The "non-matching" version
return MatchingField<N+1, T, Tuple,
VectorOfType<N+1, T>::value>::get(tp);
}
};
template <int N, class T, class Tuple>
struct MatchingField<N, T, Tuple, true>
{
static vector<T>& get(Tuple& tp)
{
return std::get<N>(tp);
}
};
template <typename T>
vector<T>& access()
{
return MatchingField<0, T, vtype,
VectorOfType<0, T>::value>::get(vectors);
}
};
Here is the testcase so you can try it out:
int main( int argc, char** argv )
{
int twelf = 12.5;
typedef reference_wrapper<int> rint;
MyClass<float, rint> mc;
vector<rint>& i = mc.access<rint>();
i.push_back(twelf);
mc.access<float>().push_back(10.5);
cout << "Test:\n";
cout << "floats: " << mc.access<float>()[0] << endl;
cout << "ints: " << mc.access<rint>()[0] << endl;
//mc.access<double>();
return 0;
}
If you use any type that is not in the list of types you passed to specialize MyClass (see this commented-out access for double), you'll get a compile error, not too readable, but gcc at least points the correct place that has caused the problem and at least such an error message suggests the correct cause of the problem - here, for example, if you tried to do mc.access<double>():
error: ‘value’ is not a member of ‘MyClass<float, int>::VectorOfType<2, double>’
An alternate solution that doesn't use tuples is to use CRTP to create a class hierarchy where each base class is a specialization for one of the types:
#include <iostream>
#include <string>
template<class L, class... R> class My_class;
template<class L>
class My_class<L>
{
public:
protected:
L get()
{
return val;
}
void set(const L new_val)
{
val = new_val;
}
private:
L val;
};
template<class L, class... R>
class My_class : public My_class<L>, public My_class<R...>
{
public:
template<class T>
T Get()
{
return this->My_class<T>::get();
}
template<class T>
void Set(const T new_val)
{
this->My_class<T>::set(new_val);
}
};
int main(int, char**)
{
My_class<int, double, std::string> c;
c.Set<int>(4);
c.Set<double>(12.5);
c.Set<std::string>("Hello World");
std::cout << "int: " << c.Get<int>() << "\n";
std::cout << "double: " << c.Get<double>() << "\n";
std::cout << "string: " << c.Get<std::string>() << std::endl;
return 0;
}
One way to do such a thing, as mentioned in πάντα-ῥεῖ's comment is to use a tuple. What he didn't explain (probably to save you from yourself) is how that might look.
Here is an example:
using namespace std;
// define the abomination
template<typename...Types>
struct thing
{
thing(std::vector<Types>... args)
: _x { std::move(args)... }
{}
void print()
{
do_print_vectors(std::index_sequence_for<Types...>());
}
private:
template<std::size_t... Is>
void do_print_vectors(std::index_sequence<Is...>)
{
using swallow = int[];
(void)swallow{0, (print_one(std::get<Is>(_x)), 0)...};
}
template<class Vector>
void print_one(const Vector& v)
{
copy(begin(v), end(v), ostream_iterator<typename Vector::value_type>(cout, ","));
cout << endl;
}
private:
tuple<std::vector<Types>...> _x;
};
// test it
BOOST_AUTO_TEST_CASE(play_tuples)
{
thing<int, double, string> t {
{ 1, 2, 3, },
{ 1.1, 2.2, 3.3 },
{ "one"s, "two"s, "three"s }
};
t.print();
}
expected output:
1,2,3,
1.1,2.2,3.3,
one,two,three,
There is a proposal to allow this kind of expansion, with the intuitive syntax: P1858R1 Generalized pack declaration and usage. You can also initialize the members and access them by index. You can even support structured bindings by writing using... tuple_element = /*...*/:
template <typename... Ts>
class MyClass {
std::vector<Ts>... elems;
public:
using... tuple_element = std::vector<Ts>;
MyClass() = default;
explicit MyClass(std::vector<Ts>... args) noexcept
: elems(std::move(args))...
{
}
template <std::size_t I>
requires I < sizeof...(Ts)
auto& get() noexcept
{
return elems...[I];
}
template <std::size_t I>
requires I < sizeof...(Ts)
const auto& get() const
{
return elems...[I];
}
// ...
};
Then the class can be used like this:
using Vecs = MyClass<int, double>;
Vecs vecs{};
vecs.[0].resize(3, 42);
std::array<double, 4> arr{1.0, 2.0, 4.0, 8.0};
vecs.[1] = {arr.[:]};
// print the elements
// note the use of vecs.[:] and Vecs::[:]
(std::copy(vecs.[:].begin(), vecs.[:].end(),
std::ostream_iterator<Vecs::[:]>{std::cout, ' '},
std::cout << '\n'), ...);
Here is a less than perfectly efficient implementation using boost::variant:
template<typename ... Ts>
using variant_vector = boost::variant< std::vector<Ts>... >;
template<typename ...Ts>
struct MyClass {
using var_vec = variant_vector<Ts...>;
std::array<var_vec, sizeof...(Ts)> vecs;
};
we create a variant-vector that can hold one of a list of types in it. You have to use boost::variant to get at the contents (which means knowing the type of the contents, or writing a visitor).
We then store an array of these variant vectors, one per type.
Now, if your class only ever holds one type of data, you can do away with the array, and just have one member of type var_vec.
I cannot see why you'd want one vector of each type. I could see wanting a vector where each element is one of any type. That would be a vector<variant<Ts...>>, as opposed to the above variant<vector<Ts>...>.
variant<Ts...> is the boost union-with-type. any is the boost smart-void*. optional is the boost there-or-not.
template<class...Ts>
boost::optional<boost::variant<Ts...>> to_variant( boost::any );
may be a useful function, that takes an any and tries to convert it to any of the Ts... types in the variant, and returns it if it succeeds (and returns an empty optional if not).