generic declaration of and acess to member variables

Pages: 12
Hi everyone,
I have a parameter N known at compile time, and a class C (from container) that contain N member variables of different types. My final objective is to work with an std::vector of C objects of a relatively big number of elements M=100 compared to N=10. I know about 2 ways to do this, template class specialization or using macros. However, these 2 approaches require lot of typing (which is error prone). I'm wondering if there are other ways to do this using new/other c++ features.
thank you for your attention.

Adim

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
#include <iostream>
#include <vector>

template<typename T, int idx>
class U
{
public:
  U()
  {
    std::cout << "call U<T, " << idx << ">::U()\n";
  }

  void update()
  {
    std::cout << "call U<T, " << idx << ">().update()\n";
  }
};

template<typename T, int N>
class C
{
private:
  U<T,0> u0;
  U<T,1> u1;
  U<T,2> u2;
//  ...
//  U<T,N> uN;

public:
  C()
  {
    std::cout << "call C<T, " << N << ">::C()\n";
  }

  void update()
  {
    std::cout << "call C<T, " << N << ">().update()\n";
    u0.update();
    u1.update();
    u2.update();
//    ...
//    uN.update();
  }
};

class T1;

int main()
{
  // step-1: generic number of C's member variables
  enum { N = 2 }; // compile-time parameter
  C<T1, N> c0;
  std::cout << "\n";

  // step-2: member function update() of class C<N>
  c0.update();
  std::cout << "\n";

#if 0
  // step-3: vector of C<N> objects
  int M = 4; // run-time variable
  std::vector< C<T1, N> > cVector(M);
  std::cout << "\n";

  std::cout << "update all vector's elements\n";
  for(int mIdx = 0; mIdx < M; ++mIdx) {
    std::cout << "mIdx = " << mIdx << "\n";
    cVector[mIdx].update();
  }
#endif

  return 0;
}
a class C (from container) that contain N member variables of different types.
The code you've posted shows a C that is analogous to an array (n elements, 1 type), not to a tuple (n elements, n types).
Actually, I could just replace your class with this:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
template<typename T, int N>
class C
{
private:
  std::array<T, N> u;

public:
  C()
  {
    std::cout << "call C<T, " << N << ">::C()\n";
  }

  void update()
  {
    std::cout << "call C<T, " << N << ">().update()\n";
    for (int i = 0; i < N; i++)
        u[i].update();
  }
};
...and all the rest of the code should work without any changes.
Hi helios,
thank you very much for your reply, actually the type of u0 is U<T,0> (not T), and the type of u1 is U<T,1> and so on until the type of uN which is U<T,N>, they are not "completely different" types like I said previously, they have some thing in common, that is why I'm looking to use this similarity to write a generic code that avoid lot of typing. The solution you propose is true only if the type of all of u0, u1, ..., uN is the same, namely T, which is not the case here.

Adim
Then how about this?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
template<typename T, int N>
class C
{
private:
  C<T, N - 1> sub;
  U<T, N - 1> u;

public:
  C()
  {
    std::cout << "call C<T, " << N << ">::C()\n";
  }

  void update()
  {
    std::cout << "call C<T, " << N << ">().update()\n";
    sub.update();
    u.update();
  }
};

template<typename T>
class C<T, 1>
{
  U<T, 0> u;

public:
  C()
  {
    std::cout << "call C<T, 1>::C()\n";
  }

  void update()
  {
    std::cout << "call C<T, 1>().update()\n";
    u.update();
  }
};
Hi,

Maybe boost::mpl int_ could be useful here.


http://www.boost.org/doc/libs/1_60_0/libs/mpl/doc/refmanual/int.html

In the following A and B are distinct types, purely because of the int template parameter:

1
2
3
4
#include <boost/mpl/int.hpp>

using A = int_<1>;
using B = int_<2>;


Note that negative values for the int are reserved for system use.

One can then use that to make a template which accepts one of those types and a type T.

I used this to make new types that had the same underlying type, but are distinguished using the int_<>.

For example, I have lots of Distance and Angle types - they all hold a solitary double value, but I want them to have distinct types so I can overload functions with them.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
#ifndef NAMEDTYPE_HPP
#define NAMEDTYPE_HPP

#include <boost/mpl/int.hpp>

namespace mpl = boost::mpl;

//TheTypes Section
using Radius = mpl::int_<1>;
using BeginAngle = mpl::int_<2>;
using EndAngle = mpl::int_<3>;
using ArcLength = mpl::int_<4>;

using EastOrdinate_t = mpl::int_<5>;
using NorthOrdinate_t = mpl::int_<6>;

using Point2D_t = mpl::int_<7>;

using ArcBeginPoint2D_t = mpl::int_<8>;
using ArcMidPoint2D_t = mpl::int_<9>;
using ArcEndPoint2D_t = mpl::int_<10>;
using CentrePoint2D_t = mpl::int_<11>;

// T is double or better,
// TheType is one of the above in TheTypes Section
template < typename TheType , typename T = double>
class NamedType
{
public:
    explicit NamedType(T x = 0.0)
       : m_value(x)
    {}
   NamedType(const NamedType& other) : m_value(other.m_value){}

   NamedType& operator= (const NamedType& other) {
//      A& operator=(A other) {
//              std::cout << "copy assignment of A\n";
//              std::swap(n, other.n);
//              std::swap(s1, other.s1);
//              return *this;
      std::swap(m_value, other.m_value);
      return *this;
   }

    // get the value
   T operator()() const {
      return m_value;
   }


   // set the value
   void operator()(T arg)  {
       m_value = arg;
   }

    T get_value() const { return m_value; }
 private:
    T m_value;
};

#endif // NAMEDTYPE_HPP 


Just wondering what your specific application is, I might be able to better explain :+)

Edit:

For my example above, I used boost::mpl::int_ to adapt the code found here:

http://www.boost.org/doc/libs/1_60_0/libs/mpl/doc/tutorial/representing-quantities.html
Last edited on
Hi Helios,
your approach is for sure better than those I thought about until now, nevertheless it has a drawback that is being incommoding me. The fact that C instances are embedded one into another does not match with the nature of the problem, it does not make any sense from the problem's point of view. That's why I would call it a workaround rather than a solution. Even though, I don't know if c++ offers the necessary tools to work out a solution in a more natural way.

kind regards,

Adim
Last edited on
Hi TheIdeasMan,
thank you very much for your concern. To be honest, it sounds scary your answer using boost, it is a library I'm not familiar with. I will try to understand your proposition and see if it fits my necessity.

Adim
Last edited on
Hi,

To be honest, it sounds scary your answer using boost, it is a library I'm not familiar with.


Boost is well worth looking at, it is written by by the C++ committee members, and has a lot of very clever/ useful things inside. Installation of boost is easy, 99% of it is header files only.

Can you share some more details about what your problem involves in a non abstract way? As I mentioned earlier I might be able to better explain whether my idea fits your problem. Sometimes an explanation in terms of the real situation is better than a description in abstract terms. Say you have solution X described in an abstract fashion A, then we hear the problem described in real fashion R, we might give solution Y because R is somehow more obvious. Solution Y might be quite different to what might have originally been imagined. At the moment IMO my idea seems to fit your problem of U<T,1...N> but that might not be the case given the real situation.

My idea is good when one has a lot things that have the same underlying type, but one needs them all to have distinct types. It makes it easier to create lots of these types, and they can be used to overload functions.

I found my idea particularly useful for Angles, Distances and mathematical Vectors/Points. Indeed, anything physics related. I am trying to extend my idea to work with boost::quantity and boost::geometry
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
#include <iostream>
#include <vector>

template< typename T, int idx > struct U
{
  U() { std::cout << "  " << idx << ". U<T," << idx << ">::U()\n"; }

  void update() { std::cout << "  " << idx << ". U<T," << idx << ">::update()\n"; }
};

template< typename T, int N > struct C : C<T,N-1>
{
    explicit C( bool noisy = true ) : C<T,N-1>(false)
    { if(noisy) std::cout << "*** C<T," << N << "> - constructed object at " << this << "\n\n" ; }

    void update( bool noisy = true )
    {
        C<T,N-1>::update(false) ;
        u.update() ;
        if(noisy) std::cout << "*** C<T," << N << "> updated object at " << this << "\n\n" ;
    }

    protected: U<T,N> u ;
};

template< typename T > struct C<T,0>
{
    explicit C( bool noisy = true )
    { if(noisy) std::cout << "*** C<T,0> - constructed object at " << this << "\n\n" ; }

    void update( bool noisy = true )
    { u.update() ; if(noisy) std::cout << "*** C<T,0> - updated object at " << this << "\n\n" ; }

    protected: U<T,0> u ;
};

struct dummy ;

int main()
{
    constexpr int N = 6 ;

    C<dummy,N> c ;
    std::cout << "------------------------\n" ;

    c.update() ;
    std::cout << "------------------------\n" ;

    int M = 4 ;
    std::vector< C<dummy,N> > vec(M) ;
    std::cout << "------------------------\n" ;

    for( auto& c : vec ) c.update() ;
}

http://coliru.stacked-crooked.com/a/3b6f970bef82335e
Thank you for joining us JLBorges, still, your answer seems to be quite similar to that of helios, it uses recursion at the C class level, making a C instance compound of other C instances of lower level (like tree branches of different levels).
However, in the problem at hand, C instances would rather be better have the same structure and the same level of complexity which is the natural way of treating the problem. This shall maintain higher design flexibility and prevent complete restructuring for future requirements changes.
Last edited on
Hi,

So can I ask again what is the real life problem here?

At the moment, your U<T,1...N> is an idea towards a solution already, and one that you have come up with to solve your problem, based on your interpretation of that problem. Now we have several implementations of that.

But I am saying there might be an even better solution, if only we knew what the real life problem was. And this solution might possibly be something quite different to what you have thought of. I am not saying saying that your existing solution is bad or anything, just that others may have different ideas.

For example helios and JLBorges might say straight away: "A-ha, you need one of these."

The abstract nature of your last reply, IMHO doesn't really help, in that we are guessing (At least I am :+), the others possibly not ) at what you really mean. Would it not be easier to just tell us the real life scenario? Perhaps there is a reason you can't or don't wish to give us something more concrete?

I hope you see this as a constructive message aimed at achieving the best solution for you :+)

Regards :+D
Last edited on
Hi TheIdeasMan,
thank you for your interest in helping me in finding a better solution, I can share with you the application at hand and will do in the following, but the issue is that I'm more interested in solving the abstract problem and learning the "how to do" more than finding a solution for the particular application. Now that you insist, hear is the scenario:
I'm writing a solver to simulate fluid flows, I have the model which is represented by the class"T", the momentum Finite Volume Geometry template class that I called "U" or rather FVG, and the Cartesian axis identified by the index "idx". For one dimensional fluid flow problems and one-scale models I have idx = 1 and the FVG<1> class that is going to be instantiated for each grid cell for the x-axis direction. For two-dimensional problems, I have idx=1 for the FVG in the x-axis direction and idx=2 for the y-axis direction. For three-dimension I have 3 classes FVG<1>, FVG<1>, FVG<3> one for each Cartesian axis. In the case of multi-scale models, I pretend to use a sequence of indices for each axis.

I hope these will be helpful,
Adim


Last edited on
Hi everyone,
Helios mentioned in his first post the use of std::tuple, it seems to be a more appropriate tool in our case, I would go to begin with the following:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
template<typename... USequence>
struct UTuple
{
  typedef std::tuple<USequence...> type;
};

template<typename T, int N>
class C
{
private:
#if 1
  typename UTuple< U<T,0>, U<T,1>, U<T,2> >::type uTuple_;
#else
  U<T,0> u0;
  U<T,1> u1;
  U<T,2> u2;
#endif

public:
  C()
  {
    std::cout << "call C<T, " << N << ">::C()\n";
  }

  void update()
  {
    std::cout << "call C<T, " << N << ">().update()\n";
#if 1
    std::get<0>(uTuple_).update();
    std::get<1>(uTuple_).update();
    std::get<2>(uTuple_).update();
#else
    u0.update();
    u1.update();
    u2.update();
#endif
  }
};


using a tuple save me the burden of giving different names for U instances and make strighforward accessing them through a template function.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
#if 1
  template<int idx>
  const U<T,idx> &u() const
  { return std::get<idx>(uTuple_); }
#else
  const U<T,0> &u0() const
  { return u0; }

  const U<T,1> &u1() const
  { return u1; }

  const U<T,2> &u2() const
  { return u2; }
#endif 


now, all what I need is to construct the following generic sequence,

 
U<T,0>, U<T,1>,..., U<T,N>


this link explain how to do to construct a sequence, but I didn't figure out yet if it can be applied to my case and how.
http://spraetor.github.io/2016/01/02/template-integer-sequence.html

What do you think about this approach?

Adim
Last edited on
Hello again,

Thanks for your explanation - IMO it's always interesting to hear about these advanced topics :+)

The following are more ideas, I hope they may be helpful and don't add confusion ....

I don't know anything about the actual topic, but I gather the axis and scale / time details can't be incorporated as attributes into a class?

I thought I would mention the boost meta programming library again. It has mpl classes such as vector which can use variadic sequences. There are intrinsic functions just like the STL with push_back , at etc; also iterators, algorithms and many other things in common with the STL.

http://www.boost.org/doc/libs/1_60_0/libs/mpl/doc/refmanual/refmanual_toc.html

Now I don't know whether this is going to turn out easier than any of the other solutions or not. But I can't help thinking that it was invented to make things easier. Examples are a bit thin on the ground, but maybe you could use this:

http://www.boost.org/doc/libs/1_60_0/libs/mpl/doc/refmanual/vector-c.html
http://www.boost.org/doc/libs/1_60_0/libs/mpl/doc/refmanual/integral-sequence-wrapper.html

Then again, perhaps you can use the boost::mpl::vector with a for loop to push_back instances of U<T,N> where N is 0 to 100 say.

I should have a go at implementing this, but I have some other stuff to do first.

Anyway just throwing out ideas, not a clue as to whether any of them will stick or not :+)

Regards

Hi everyone,
here we go,
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
#include <iostream>
#include <vector>
#include <tuple>

// step-1: make the sequence U<T,0>, U<T,1>,..., U<T,N>
template<typename... Arg>
struct Sequence { using Tuple = std::tuple<Arg...>; };
//
template<typename S, typename LastArg>
struct PushBackArg;

template<typename... Arg, typename LastArg>
struct PushBackArg<Sequence<Arg...>, LastArg> { using type = Sequence<Arg..., LastArg>; };
//
template<template<typename,int> typename GenericArg, typename T, int N>
struct MakeSequence { using type = typename PushBackArg<typename MakeSequence<GenericArg,T,N-1>::type, GenericArg<T,N> >::type; };

template<template<typename,int> typename GenericArg, typename T>
struct MakeSequence<GenericArg,T,0> { using type = Sequence< GenericArg<T,0> >; };

// step-2: access and change std::tuple members
template<typename T>
constexpr std::make_index_sequence<std::tuple_size<T>::value>
get_indexes_(T const& )
{ return {}; }
//
template<typename Func, typename...Arg, std::size_t...idx>
void for_each_in_tuple(std::tuple<Arg...> &tup, Func func, std::index_sequence<idx...>)
{
  using expander = int[];
  (void)expander { 0, ((void)func(std::get<idx>(tup)), 0)... };
}

template<typename Func, typename...Arg>
void for_each_in_tuple(std::tuple<Arg...> &tup, Func func)
{
  for_each_in_tuple(tup, func, std::make_index_sequence<sizeof...(Arg)>());
}

// step-3: define the C class
template<template<typename,int> typename GenericArg, typename T, int N>
class C
{
private:
  typedef typename MakeSequence<GenericArg,T,N>::type::Tuple Tuple;
  Tuple tup_;

public:
  C()
  {
    std::cout << "call C<T, " << N << ">::C()\n";
    std::cout << "std::tuple_size<Tuple>::value = " << std::tuple_size<Tuple>::value << '\n';
  }

  void update()
  {
    std::cout << "call C<T, " << N << ">().update()\n";
    for_each_in_tuple(tup_, [](auto &arg) { arg.update(); });
  }

  template<int idx>
  const GenericArg<T,idx> &arg() const
  { return std::get<idx>(tup_); }
};

// step-4: define the U class
template<typename T, int idx>
class U
{
public:
  U()
  {
    std::cout << "call U<T, " << idx << ">::U()\n";
  }

  void update()
  {
    std::cout << "call U<T, " << idx << ">().update()\n";
  }
};
//
class Dummy;
//
int main()
{
  enum { N = 2 }; // compile-time parameter

  // step-1: instanciate C
  C<U,Dummy, N> c0;
  std::cout << "\n";

  // step-2: access C members and update
  c0.update();
  std::cout << "\n";

#if 1
  // step-3: vector of C<N> objects
  int M = 4; // run-time variable
  std::vector< C<U,Dummy, N> > cVector(M);
  std::cout << "\n";

  std::cout << "update all vector's elements\n";
  for(int mIdx = 0; mIdx < M; ++mIdx) {
    std::cout << "mIdx = " << mIdx << "\n";
    cVector[mIdx].update();
  }
#endif

  return 0;
}


please let me know if there are rooms for improvement.

Adim
Last edited on
Hi TheIdeasMan,
thank you for the links, I will see what I can get from them. There must exist some algorithms that can spare me lot of writing, shorten the steps 1 and 2 mentioned above in particular .

Adim
Last edited on
Fusion http://www.boost.org/doc/libs/1_60_0/libs/fusion/doc/html/fusion/preface.html

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
#include <iostream>
#include <tuple>
#include <boost/fusion/adapted/std_tuple.hpp> 
// http://www.boost.org/doc/libs/1_60_0/libs/fusion/doc/html/fusion/adapted/std__tuple.html

#include <boost/fusion/algorithm/iteration/for_each.hpp> 
// http://www.boost.org/doc/libs/1_60_0/libs/fusion/doc/html/fusion/algorithm/iteration/functions/for_each.html

#include <vector>

template < template< typename, std::size_t > class G, typename T, std::size_t N >
struct make_sequence
{
    using type = decltype( std::tuple_cat( std::declval< typename make_sequence<G,T,N-1>::type >(),
                                           std::declval< std::tuple< G<T,N> > >() ) );
};

template < template< typename, std::size_t > class G, typename T >
struct make_sequence<G,T,0> { using type = std::tuple< G<T,0> > ; };

template < typename FN, typename... T >
void for_each_in_tuple( std::tuple<T...>& tup, FN fn ) { boost::fusion::for_each( tup, fn ) ; }

template < template< typename, std::size_t > class G, typename T, std::size_t N > struct C
{
    typename make_sequence< G, T, N >::type tup ;

    struct do_update { template < typename X > void operator() ( X& x ) const { x.update() ; } };
    void update() { for_each_in_tuple( tup, do_update() ) ; }
};

template< typename T, std::size_t N > struct U
{ void update() { std::cout << "U<T," << N << ">::update()\n" ; } };

int main()
{
    struct dummy ;
    C< U, dummy, 8 > c ;
    c.update() ;

    std::cout << "\n\n---------------\n\n" ;

    std::vector< C< U, dummy, 2 > > vec(4) ;
    for( auto& t : vec ) { t.update() ; std::cout << '\n' ; }
}

http://coliru.stacked-crooked.com/a/60bbd315cd1caf0c
Hi,

I reckon you may not need to have a container of types, here is my reasoning:

You start with the need for lots of types for the problem at hand. This leads to the idea of U<T,1...N> all good so far. But then we have the idea of all those types living in a container. This is the part which I think may not be necessary.

I think it might be better to have meaningful names for each of the types, and hide the underlying integer used to create the distinct type. If my understanding is correct, I guess that you are not going to iterate through the container of types, rather each cell in the world model has a particular type. So, if non iteration of the types themselves is actually the case, there may be no need for a container.

With the "burden" of having individual names for the types, I look at that in the opposite way: having a meaningful name is better than having to remember what the 40 in U<T,40> means. I don't see it as a problem to have 100 types in a header file, mainly because at the moment I don't see the benefit of having them in a container.

So, may be you could just stick with :

1
2
3
 U<T,0> XAxis;
  U<T,1> YAxis;
  U<T,2> ZAxis;


then :

auto Analyse (XAxis, YAxis, ZAxis);

At the moment IMO this looks a lot easier than going through the hassle of creating a sequence.

However I did this, as a variation on what I originally posted, it has the advantage that one can create their own types, rather than explicitly doing in a template. It is pretty bare bones:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#ifndef MODEL_H
#define MODEL_H

class Model
{
private:
    double DataA = 17.0;
    double DataB = 237.6;
public:
    Model() = default;
    Model(const double DataAarg, const double DataBarg);

    double Calculate() const;
};

#endif // MODEL_H 


1
2
3
4
5
6
7
8
9
10
11
#include "Model.hpp"

Model::Model(const double DataAarg,
             const double DataBarg)
            :
              DataA(DataAarg),
              DataB(DataBarg){}

double Model::Calculate() const {
    return DataA * DataB;
}



1
2
3
4
5
6
7
8
9
10
11
12
13
#ifndef TYPES_HPP
#define TYPES_HPP

#include <boost/mpl/int.hpp>

namespace mpl = boost::mpl;

using XAxis_t = mpl::int_<1>;
using YAxis_t = mpl::int_<2>;
using ZAxis_t = mpl::int_<3>;


#endif // TYPES_HPP 


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#ifndef NAMEDTYPE_HPP
#define NAMEDTYPE_HPP

#include "Types.hpp"


// TheType is one of the types in Types.hpp file
// T is the underlying class that we want a distinct type for
template < typename TheType , typename T >
struct NamedType
{

    T Object;
    NamedType() = default;
    explicit NamedType(T Arg)
       : Object(Arg)
    {}

};

#endif // NAMEDTYPE_HPP 



1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#include <iostream>

#include "Model.hpp"
#include "NamedType.hpp"

int main()
{
    NamedType<XAxis_t, Model> XInfo;
    NamedType<YAxis_t, Model> YInfo;
    NamedType<ZAxis_t, Model> ZInfo;

    std::cout << XInfo.Object.Calculate() << "\n";
    std::cout << YInfo.Object.Calculate() << "\n";
    std::cout << ZInfo.Object.Calculate() << "\n";

    return 0;
}
}



I did try using boost::mpl::vector and variadic sequences, but I couldn't get anything better than what we had already. The problem was that everything in TMP is a type, so push_back creates a new type, but it goes out of scope of the for loop. Variadic sequences would work, but I was no further ahead if I had to individually create types. Maybe I don't know enough about it. Anyway that is what lead me to think a container wasn't needed.

Regards :+)
Last edited on
Corrected version of code using boost::fusion (make_sequence<> - added std::remove_reference)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
#include <iostream>
#include <tuple>
#include <type_traits>
#include <boost/fusion/adapted/std_tuple.hpp>
#include <boost/fusion/algorithm/iteration/for_each.hpp>
#include <vector>

template < template< typename, std::size_t > class G, typename T, std::size_t N >
struct make_sequence
{
    using type = typename std::remove_reference< // *** EDIT: added remove_reference
                     decltype( std::tuple_cat( std::declval< typename make_sequence<G,T,N-1>::type >(),
                                               std::declval< std::tuple< G<T,N> > >() ) ) >::type ;
};

template < template< typename, std::size_t > class G, typename T >
struct make_sequence<G,T,0> { using type = std::tuple< G<T,0> > ; };

template < template< typename, std::size_t > class G, typename T, std::size_t N > struct C
{
    typename make_sequence< G, T, N >::type tup ;

    struct do_update { template < typename X > void operator() ( X& x ) const { x.update() ; } };
    void update() { boost::fusion::for_each( tup, do_update() ) ; }
};

template< typename T, std::size_t N > struct U { void update() { std::cout << "U<T," << N << ">::update()  " ; } };

int main()
{
    struct dummy ;
    std::vector< C< U, dummy, 4 > > vec(8) ;
    for( auto& t : vec ) { t.update() ; std::cout << "(tuple at " << std::addressof(t) << ")\n" ; }
    std::cout << "sizeof( vec.front().tup ) == " << sizeof( vec.front().tup ) << '\n' ; // empty member optimisation!
}

http://coliru.stacked-crooked.com/a/d1bcea0155ad9dc0
Last edited on
Oi JLBorges,
amazing job, this is what I was looking for, thank you vary much!
Still, before marking the problem as solved, I'm wondering if I can do better! I'm trying to reduce dependencies, in particular the one between make_sequence and its specialization by using std::conditional (note that I didn't had success with this, it seems that std::conditional evaluate the 2 involved types what ever the condition being true or false, by consequence it keeps stuck in the recursion evaluating make_sequence<G,T,N-1>::type).
The other dependency concerns the update() function and do_update struct. The structure do_update and update() are dependent and do_update does not have any use other than being called by update(). Also, we can't get ride of do_update if we want to use boost::fusion::for_each (we need a constant operator to be passed by reference). Thus, if I can't get rid of do_update, what about making it generic (let's call it apply) and passing a typename Function as template argument. This way, it is going to be more useful and I will be able to use it with an arbitrary function other than update(), for example reset(). Today, I did not have enough time to work on it, but it must be something like (PS. the following peace of code does not compile.),

1
2
3
4
5
6
7
8
9
10
11
12
13
14
 
template<typename Fn>
struct apply { template < typename X > void operator() (X& x, Fn &fn) const { (x.*fn)() ; } };

void update() { boost::fusion::for_each( tup, apply<&G<T,N>::update>() ) ; }

void reset() { boost::fusion::for_each( tup, apply<&G<T,N>::reset>() ) ; }


template< typename T, std::size_t N > struct U
{
  void update() { std::cout << "U<T," << N << ">::update()\n" ; }
  void reset() { std::cout << "U<T," << N << ">::reset()\n" ; }
};


Adim
Last edited on
Pages: 12