Question

I have some implementer classes (impls) and some wrappers for user, implemented in C++. I want to hold impls and wrappers in two different tuples so that i can initialize my impls by a single allocation. (I have other reasons too:).

The thing is tuple class of visual studio 2012 standard library does not allow me to construct my wrapper tuple without a const referenced copy constructor of wrappers. sadly I need to const_cast in that case, such as:

#include <iostream>
#include <type_traits>
#include <tuple>
#include <typeinfo>

template <typename Member>
struct A
{
    A(Member& m) : member(m)
    { std::cout << typeid(Member).name() << " MMBR " << member << std::endl; }

    A(const Member& m) : member(const_cast<Member&>(m))
    { std::cout << typeid(Member).name() << " CMBR " << member << std::endl; }

    void Print()
    {
        std::cout << typeid(Member).name() << " PRNT " << member << std::endl;
    }

    Member& member;//yes I need to hold this as a mutable reference
};

int main()
{
    typedef std::tuple<A<int>, A<double>, A<short>> WrapperTuple;
    typedef std::tuple<int, double, short> Tuple;

    Tuple t(0, 1, 2);
    WrapperTuple w(t);

    std::get<1>(w).Print();
    return std::cin.get();
}

The code above compiles and runs as intended, but if I delete/comment out the const-ref-ctor of wrapper class A neither my VS2012 compiler nor my gcc4.7.2 compiler compiles the code. (1) What am I doing wrong?

Since I don't have a good documentation for c++11, I guess that variadic copy ctor of tuple only takes a const ref of other tuple. If so, (2) why doesn't tuple class have such ctor? I mean the main reason behind.

To sum up I want to hold all impls and wrappers together in a tuple so I can allocate with a single action (i.e. make_shared). Tuple is somewhat a must because I have written some helpers so that I can lookup by type in compile time (e.g. Get<A<int>>(w)) (3) Is there a neat way to hold a reference to the impl such that I would not need to allocate each impl separately.

Was it helpful?

Solution

The copy constructor of std::tuple, even the converting one, obviously copies all the elements, and since a copy shouldn't change the copied-from element, they're marked as const. This behaviour is perfectly reasonable, most of the time.

The workaround for your special case is a bit more involved that what you may like, but it works. The basic idea is that you, conceptually, don't want to copy the tuple, but you want to use it's elements as an initializer to the elements of your other tuple, as such their constness should be preserved.

template<unsigned...> struct seq{};
template<unsigned N, unsigned... Is>
struct gen_seq : gen_seq<N-1, N-1, Is...>{};
template<unsigned... Is>
struct gen_seq<0, Is...> : seq<Is...>{};

namespace aux{
template<class... Ts, unsigned... Is>
std::tuple<Ts&...> tie_all_(std::tuple<Ts...>& other, seq<Is...>){
  return std::tie(std::get<Is>(other)...);
}
} // aux::

template<class... Ts>
std::tuple<Ts&...> tie_all(std::tuple<Ts...>& other){
  return aux::tie_all_(other, gen_seq<sizeof...(Ts)>());
}

The code is used like this: WrapperTuple w(tie_all(t));. Now you can get rid of the Member const& constructor.

You could even go further and write a function that turns a tuple into a wrapper tuple, thus getting rid of having to specify the type manually:

template<class... Ts>
std::tuple<A<Ts>...> wrap_all(std::tuple<Ts...>& other){
  return tie_all(other);
}
// ...
auto w(wrap_all(t));

And if you have different wrapper classes:

template<template<class> class Wrapper, class... Ts>
std::tuple<Wrapper<Ts>...> wrap_all_in(std::tuple<Ts...>& other){
  return tie_all(other);
}
// ...
auto w = wrap_all_in<A>(t);

OTHER TIPS

Why don't you just create wrapped_tuple template that wraps the whole tuple and provides an implementation of get? It seems redundant to hold references to individual tuple elements, since the tuple has a fixed layout and the compiler can emit trivial code to reference an individual element given a reference to the tuple.

For example (and doing this without variadic templates, which is a bit annoying):

template<typename Tuple> class wrapped_tuple;

template<size_t I, typename Tuple>
typename std::tuple_element<I, Tuple>::type&
Get(wrapped_tuple<Tuple>& w) {
  return std::get<I>(w.tuple_);
}

template<typename Tuple> class wrapped_tuple {
  template<size_t I, typename Uple>
  friend typename std::tuple_element<I, Uple>::type&
         ::Get(wrapped_tuple<Uple>& w);
  public:
    wrapped_tuple(Tuple& t) : tuple_(t) {}
  private:
    Tuple& tuple_;
};

template<typename Tuple>
wrapped_tuple<Tuple> wrap_tuple(Tuple& tup) {
  return wrapped_tuple<Tuple>(tup);
}

Here on ideone.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top