found a use in union ctors and dtors

1
2
3
4
5
6
7
8
9
10
11
template<int N=1>
union slices
{
	int k[N];
	slices<N-1> tranche;
	slices(){k[N-1]=N;}
};
template<>
union slices<1>
{int k[1];struct{} tranche;};
slices<5> s;

the idea is to have ctor and dtor for c-style arrays of primitive types.
above code only works with gcc and -std=c++0x
has anybody else found useful ctors for unions?
I could be argued that a union with one field is the trivial case of a union and in principle isn't any different from a struct with one field. But I don't think that means its the right tool. But it is an interesting trick.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
template<typename To, typename From>
union Reinterpret_Cast
{
    Reinterpret_Cast(From f) : f(f)
    {
    }
    operator To()
    {
        return t;
    }
private:
    From f;
    To t;
};
Doesn't quite work like you would hope, you have to pass the second template parameter anyway:
http://ideone.com/CI9dgu
If it is C++11, and default value initialization of c-style arrays is required , why not something like 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
#include <type_traits>
#include <iterator>
#include <iostream>

template < typename T, std::size_t N > struct c_array
{
    static_assert( std::is_pod<T>::value, "must be a pod!" ) ;
    static_assert( N, "size must be a non-zero!" ) ;

    c_array() : a { T() } {}
    template < typename... U > c_array( U&&... args ) : a { args... } {} // ??

    T& operator[] ( std::size_t p ) { return a[p] ; }
    const T& operator[] ( std::size_t p ) const { return a[p] ; }

    T* begin() { return std::begin(a) ; }
    T* end() { return std::end(a) ; }
    const T* begin() const { return std::begin(a) ; }
    const T* end() const { return std::end(a) ; }

    T a[N] ;
};

int main()
{
    c_array<int,5> a ; // value initialized
    for( auto i : a ) std::cout << i << ' ' ; std::cout << '\n' ;

    c_array<long,6> b { 1, 2, 3, 4 };
    for( auto i : b ) std::cout << i << ' ' ; std::cout << '\n' ;
}


http://ideone.com/cJ4TdD
problem with this new c++11 syntax is that some objects require array-initialization depending on the array-index! with a constructor of a union you can even do the initialization inside of the initialization list instead of using the ctor-body. only problem that needs to be solved before this can be included into a standard is that a union has strict requirements for the predictability of what order all ctors are executed in. an array of structs would need to avoid executing ctor of that struct-array alltogether and instead use the ctor provided in the union. this would be a different behaviour from usual objects. btw, a problem with my program is I forgot to put the default-ctor into the initialization-list -- explains why it did throw an error...

as for union of a single element, imho this would be a neat trick to avoid that element's ctor, if the compiler would support that...

the reinterpret cast is a nice trick too, how about:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
template<typename From>
union IsNegative
{
    IsNegative(From f) : f(f)
    {
    }
    operator bool()
    {
        return t.sign;
    }
private:
    From f;
    struct {From forgetit : sizeof(From)*8-1;bool sign;} t;
};
or something similar for floats and their radix...
Last edited on
> problem with this new c++11 syntax is that some objects require array-initialization depending on the array-index!

What is the problem? Use a std::initializer_list<> of position-value pairs.

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
#include <type_traits>
#include <iterator>
#include <iostream>
#include <utility>
#include <initializer_list>

template < typename T, std::size_t N > struct c_array
{
    static_assert( std::is_pod<T>::value, "must be a pod!" ) ;
    static_assert( N, "size must be a non-zero!" ) ;

    c_array() : a { T() } {}
    template < typename... U > c_array( U&&... args ) : a { args... } {} // ??

    c_array( std::initializer_list< std::pair<std::size_t,T> > il ) : c_array<T,N>()
    { for( const auto& p : il ) if(p.first<N) a[p.first] = p.second ; }

    T& operator[] ( std::size_t p ) { return a[p] ; }
    const T& operator[] ( std::size_t p ) const { return a[p] ; }

    T* begin() { return std::begin(a) ; }
    T* end() { return std::end(a) ; }
    const T* begin() const { return std::begin(a) ; }
    const T* end() const { return std::end(a) ; }

    T a[N] ;
};

int main()
{
    c_array<int,5> a ; // value initialized
    for( auto i : a ) std::cout << i << ' ' ; std::cout << '\n' ;

    c_array<long,6> b { 1, 2, 3, 4 };
    for( auto i : b ) std::cout << i << ' ' ; std::cout << '\n' ;

    // initialize by position, and value initialize unspecified positions
    c_array<short,8> c { { 1, 8 }, { 3, 9 }, { 5, 2 }, { 6, -1 } };
    for( auto s : c ) std::cout << s << ' ' ; std::cout << '\n' ;
}


http://ideone.com/HYEW1Y
the problem is that I need to do that in the ctor, and not in main()!
in the initialization list I'd like to have N passed from the template parameter, to use that for calculating the actual value I want to write into the array at index N-1. or maybe even better use the previous values at 0..N-2 for calculating a new N-1 value. this way it finally is possible to initialize an array of "const int" inside a ctor, depending on the ctor-parameters...

in your example just change the last line of struct c_array from
T a[N] ;
to
1
2
    typedef const T val;
    val a[N] ;
(or simply pass "const short" as template parameter in your new pair-declaration) and you will know what I mean...
If the values need to be constexpressions, we could write a wrapper to construct a boost::mpl::vector_c<>

If not:
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
#include <type_traits>
#include <iterator>
#include <iostream>
#include <utility>
#include <initializer_list>

template < typename T, std::size_t N > struct c_array
{
    static_assert( std::is_pod<T>::value, "must be a pod!" ) ;
    static_assert( N, "size must be a non-zero!" ) ;

    c_array() : a { T() } {}
    template < typename... U > c_array( U&&... args ) : a { args... } {} // ??

    c_array( std::initializer_list< std::pair<std::size_t,T> > il ) : c_array<T,N>()
    { for( const auto& p : il ) if(p.first<N) a[p.first] = p.second ; }

    T& operator[] ( std::size_t p ) { return a[p] ; }
    const T& operator[] ( std::size_t p ) const { return a[p] ; }

    T* begin() { return std::begin(a) ; }
    T* end() { return std::end(a) ; }
    const T* begin() const { return std::begin(a) ; }
    const T* end() const { return std::end(a) ; }

    private : typename std::remove_const<T>::type a[N] ;
};

int main()
{
    c_array<const int,5> a ; // value initialized
    for( auto i : a ) std::cout << i << ' ' ; std::cout << '\n' ;

    c_array<long,6> b { 1, 2, 3, 4 };
    for( long& i : b ) std::cout << i << ' ' ; std::cout << '\n' ;

    // initialize by position, and value initialize unspecified positions
    const c_array<const short,8> c { { 1, 8 }, { 3, 9 }, { 5, 2 }, { 6, -1 } };
    for( auto s : c ) std::cout << s << ' ' ; std::cout << '\n' ;
}
Last edited on
well, my version looks like that:
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
#include <boost/mpl/map.hpp>
#include <boost/mpl/int.hpp>

template<class T, int N, class out>
union TemplateToArray
{
	T first,all[N];
	struct {T drop;TemplateToArray<T,N-1,typename out::next> rem;} cut;
	TemplateToArray() : first(out::ret), cut() {}
};
template<class T, class out>
union TemplateToArray<T,1,out>
{
	T first,all[1];
	struct {T drop;union{} rem;} cut;
	TemplateToArray() : first(out::ret) {}
};
template<class IN,int AT>
struct ReadMap
{
	enum {ret=boost::mpl::if_<
			boost::mpl::has_key<IN,boost::mpl::int_<AT> >,
			boost::mpl::at<IN,boost::mpl::int_<AT> >,
			boost::mpl::int_<0> >::value};
	typedef ReadMap<IN,AT-1> next;
};
typedef boost::mpl::map< 
		boost::mpl::pair< boost::mpl::int_<1>, boost::mpl::int_<8> >,  
		boost::mpl::pair< boost::mpl::int_<3>, boost::mpl::int_<9> >,
		boost::mpl::pair< boost::mpl::int_<5>, boost::mpl::int_<2> >,
		boost::mpl::pair< boost::mpl::int_<6>, boost::mpl::int_<-1> > > _Input;
typedef TemplateToArray<const int,8,ReadMap<_Input,5> > Reader;

and the resulting union would ideally contain all the numbers from input at the specified positions like in your example, except they are stored as "const int"! std::remove_const is a cheap trick and does not do any good. c++ has a special functionality where you can overload a member-function with the same input but different output type, and the choice between the two functions is made at compile-time by checking if the object (or an object containing it as a member) is constant. so std::remove_const would break that functionality!
Last edited on
> ... and the choice between the two functions is made at compile-time by checking
>if the object (or an object containing it as a member) is constant.
> so std::remove_const would break that functionality!

Would it?

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

struct A
{
    void foo() { std::cout << "A::foo()\n" ; }
    void foo() const { std::cout << "A::foo() const\n" ; }
};

template < typename T > struct B
{
    void bar() { const_cast<T&>(data).foo() ; }

    private: typename std::remove_const<T>::type data ;
};

int main()
{
    B<A> x ; x.bar() ; // A::foo()

    B<const A> y ; y.bar() ; // A::foo() const
}


http://ideone.com/RnYhYP
well, if you choose to add const_cast everytime you access that data, especially every time you access the methods in data, then you wouldn't break anything, true. however, then you cannot give direct member-access, you must either mirror all members in your wrapper, or you must pass a reference to array and the array-elements through for example some operator[]. quite inconvenient to debug imho, and error-prone.

my previous program was a bit buggy, this one actually works:
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
#include <boost/mpl/map.hpp>
#include <boost/mpl/at.hpp>
#include <boost/mpl/int.hpp>

template<class T, int N, class out>
union TemplateToArray
{
	T a[N];
	struct It
	{
		T drop;
		TemplateToArray<T,N-1,typename out::next> rem;
		It() : rem(), drop(out::ret) {}
	};
	It cut;
	TemplateToArray() : cut() {}
};
template<class T, class out>
union TemplateToArray<T,1,out>
{
	T first, a[1];
	struct {T drop;union{} rem;} cut;
	TemplateToArray() : first(out::ret) {}
};

template<class IN,int AT>
struct ReadMap
{
	typedef typename boost::mpl::int_<AT>::type where;
	typedef typename boost::mpl::has_key<IN, where>::type has_key;
	typedef typename boost::mpl::at<IN,where >::type lookup;
	typedef typename boost::mpl::int_<0>::type zero;
	typedef typename boost::mpl::if_<has_key, lookup, zero>::type type;
	enum {ret=type::value};
	typedef ReadMap<IN,AT+1> next;
};
typedef boost::mpl::map< 
		boost::mpl::pair< boost::mpl::int_<1>, boost::mpl::int_<8> >,  
		boost::mpl::pair< boost::mpl::int_<3>, boost::mpl::int_<9> >,
		boost::mpl::pair< boost::mpl::int_<5>, boost::mpl::int_<2> >,
		boost::mpl::pair< boost::mpl::int_<6>, boost::mpl::int_<-1> > > Input_;
TemplateToArray<const int,8,ReadMap<Input_,0> > Reader;
> well, if you choose to add const_cast everytime you access that data,
> especially every time you access the methods in data, then you wouldn't break anything, true.
> however, then you cannot give direct member-access, you must either mirror all members ...

All that is required to give the illusion of direct member-access is a bit of trivial syntactic sugar:

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

struct A
{
    void foo() { std::cout << "A::foo()\n" ; }
    void foo() const { std::cout << "A::foo() const\n" ; }
};

template < typename T > struct B
{
    private: typename std::remove_const<T>::type private_data ;
    public: T& public_data = private_data ;
};

int main()
{
    B<A> x ; x.public_data.foo() ; // A::foo()

    B<const A> y ; y.public_data.foo() ; // A::foo() const
}
I think you are getting confused with some other programming-language. in c++ the reference is syntactic sugar for a pointer!
1
2
3
int i=5;
int &ri=i;
int *pi=i;
here the last 2 lines are equivalent after compiletime!

so even if you could actually initialize the reference inside of the class with "=", which wont work since at compiletime the actual position of the thing you want to reference is not yet known, your "syntactic sugar" would at run-time cost you the size of a pointer! for example in
1
2
3
	class A {short a;public: short get(){return a;}} d1;
	class B {short a;public: short& b;const short get()const{return a;}B():b(a){}} d2;
	class C {short a;public: const short& b;const short get()const{return a;}C():b(a){}} d3;
sizeof(A) is 2, sizeof(B) and sizeof(C) is 16...
> in c++ the reference is syntactic sugar for a pointer!

No.


> here the last 2 lines are equivalent after compiletime!

No.


> so even if you could actually initialize the reference inside of the class with "="

"Even if you could"? You can.

Btw, specifying an initializer is different from initializing.


> which wont work since at compile time etc. ...

With standard C++, it does.
http://ideone.com/RAbNyA


> your "syntactic sugar" would at run-time cost you the size of a pointer!

If the size of a pointer is made out to be the major concern (in a class which is supposed to hold an array), the work-around to eliminate that is also as trivial:

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

struct A
{
    void foo() { std::cout << "A::foo()\n" ; }
    void foo() const { std::cout << "A::foo() const\n" ; }
};

template < typename T > struct B
{
    private: typename std::remove_const<T>::type private_data ;
    public: inline T& public_data() { return private_data ; }
};

int main()
{
    B<A> x ; x.public_data().foo() ; // A::foo()

    B<const A> y ; y.public_data().foo() ; // A::foo() const
}



Note: Everything that I've written in this thread has been based on the assumption that the overriding design goal was not 'I must use a union, somehow, anyhow. I must, I must'. As things have panned out, that has turned out to be an apparently naive assumption.

sorry, you are right, you meant "reference to member" I was confused since usually I write "B::private_data" in that case to distinguish the two. unfortunately my compiler doesn't allow specifying an initializer, only initializing is possible and there some member must actually exist to get initialized. since it works for your version, what does sizeof(B<A>) say?

and no, this is not about "I must use union". things have panned out because my original goal was to take the output of some boost::mpl functions and stuff it into an actual array by initializing each array-member to the constant value returned by boost::mpl -- obviously I was too subtle in clarifying that above. for example to program a division-algorithm I must have some pre-calculated division-table and look up partial results in the program to glue them together. i.e. I want that division-table in a constant static class-member shared by all number-objects. you still haven't convinced me it is possible to do that initialization without a union, because you haven't given an example where boost::mpl output gets copied into the array! of course the hope is that if the compiler notices the initializer is fed constants it will create that object at compile-time when optimized, so I save on startup-time...

to summarize: we can agree that for an ordinary container no union is needed, in c++11.

so I ask you: can you do the same I did do with union, feeding the values stored in boost::mpl::map to the initializer of each array-element, but without using a union?
piotr5 wrote:
unfortunately my compiler doesn't allow specifying an initializer
What kind of crazy compiler do you use that supports C++11 features but not this feature from before C++11?
> unfortunately my compiler doesn't allow specifying an initializer

>> What kind of crazy compiler do you use that supports C++11 features but not this feature from before C++11?

In-class member initializer


> my original goal was to take the output of some boost::mpl functions and stuff it into an actual array
> by initializing each array-member to the constant value returned by boost::mpl

If these are constants known at compile time, perhaps you should just use a boost::mpl::vector_c<> instead of an array. Particularly since you are very concerned about storage and startup-time requirements.

Stuffing values into a constexpr array requires a bit of boiler plate code; for brevity the snippet below uses metaprogramming support for variadic templates (utils::vtmp::iota<>, utils::vtmp::integers<>) provided by the vtmp library (Boost License, clean non-viral)

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
#include <iostream>
#include <boost/mpl/size_t.hpp>
#include <boost/mpl/int.hpp>
#include <boost/mpl/vector_c.hpp>
#include <boost/mpl/push_back.hpp>
#include <boost/mpl/advance.hpp>
#include <boost/mpl/erase.hpp>
#include <boost/mpl/insert.hpp>
#include <boost/mpl/for_each.hpp>
#include <boost/mpl/range_c.hpp>

template < std::size_t SZ > struct init_vec
{
    using zero = boost::mpl::int_<0> ;
    using type = typename boost::mpl::push_back< typename init_vec<SZ-1>::type, zero >::type ;
} ;

template <> struct init_vec<1>
{ using type = boost::mpl::vector_c<int,0> ; } ;

template < typename VEC, std::size_t POS, int VALUE > struct replace
{
    using POS_TYPE = boost::mpl::size_t<POS> ;
    using VALUE_TYPE = boost::mpl::int_<VALUE> ;
    using BEGIN = typename boost::mpl::begin<VEC>::type ;
    using ITER = typename boost::mpl::advance< BEGIN, POS_TYPE >::type ;
    using TEMP = typename boost::mpl::erase<VEC,ITER>::type ;
    using BEGIN2 = typename boost::mpl::begin<TEMP>::type ;
    using ITER2 = typename boost::mpl::advance< BEGIN2, POS_TYPE >::type ;
    using type = typename boost::mpl::insert<TEMP,ITER2,VALUE_TYPE>::type ;
};

struct print
{
    template< typename T > void operator() ( T v ) const
    { std::cout << v << ' '; }
};

#ifdef HAVE_KENNYTM_UTILS
///// ***** requires use of https://github.com/kennytm/utils *****
#include "utils/vtmp.hpp" // https://github.com/kennytm/utils/blob/master/vtmp.hpp
#include <array>
#include <boost/mpl/size.hpp>
#include <boost/mpl/at.hpp>

template< typename VEC > struct A
{
    static constexpr size_t SZ = boost::mpl::size<VEC>::value ;
    typedef std::array< const int, SZ > array_type ;

    template< size_t... POS > static constexpr array_type
            make_array( const utils::vtmp::integers<POS...>& )
    { return array_type {{ boost::mpl::at_c<VEC,POS>::type::value... }}; }

    static constexpr array_type division_table()
    { return make_array( utils::vtmp::iota<SZ> {} ) ; }
};
//////////////////////////////////////////////////////////////////////
#endif // HAVE_KENNYTM_UTILS

int main()
{
    constexpr std::size_t SZ = 10 ;
    using v1 = init_vec<SZ>::type ;
    using v2 = replace< v1, 2, 8 >::type ;
    using v3 = replace< v2, 4, 9 >::type ;
    using v4 = replace< v3, 5, 7 >::type ;
    using vec = replace< v4, 7, 3 >::type ;

    std::cout << "positions: " ;
    boost::mpl::for_each< boost::mpl::range_c<int,0,SZ> >( print() ) ;
    std::cout << "\n   values: " ;
    boost::mpl::for_each<vec>( print() ) ;
    std::cout << '\n' ;

#ifdef HAVE_KENNYTM_UTILS
    std::cout << "div table: " ;
    for( int v : A<vec>::division_table() ) std::cout << v << ' ' ;
    std::cout << '\n' ;
#endif // HAVE_KENNYTM_UTILS
}


Output (with -DHAVE_KENNYTM_UTILS)
positions: 0 1 2 3 4 5 6 7 8 9
   values: 0 0 8 0 9 7 0 3 0 0
div table: 0 0 8 0 9 7 0 3 0 0
Last edited on
Topic archived. No new replies allowed.