6

I would like to allow use of the class I'm writing to specify as a template parameters a list of types along with a list of allocators of those types in a manner that types are at odd positions and allocators are at even ones:

template<typename... T>
class MyClass {
  // Stuff inside
}

int main() {
  MyClass<SomeType1, AllocatorOfSomeType1> c1;
  MyClass<SomeType1, AllocatorOfSomeType1, 
          SomeType2, AllocatorOfSomeType2> c2;
  MyClass<SomeType1, AllocatorOfSomeType1, 
          SomeType2, AllocatorOfSomeType2,
          SomeType3, AllocatorOfSomeType3> c3;
  // And so on....
}

Internally it would make sense to have a tuple of vectors of types for storage:

std::tuple<std::vector<EveryOddTypeInParameterPack>...> m_storage_;

and a tuple of allocators for usage:

std::tuple<std::vector<EveryEvenTypeInParameterPack>...> m_storage_;

How can I actually declare those tuples in code? In theory I need to somehow select every odd/even type in parameter pack - is that possible?

Bartłomiej Siwek
  • 1,447
  • 2
  • 17
  • 26
  • 1
    @Alf Could you elaborate a bit more on the whole "why" thing? If you are asking why I want this interface - because I need to provide users of this class with a convenient way to specify allocators. I'm open to (reasonable) alternatives tho. – Bartłomiej Siwek Jun 25 '11 at 03:16
  • 4
    You could just use the allocators as your template arglist, and use allocator::value_type for the types... – Chris Dodd Jun 25 '11 at 03:40
  • I'm tempted to try something like the old `isEven(n) { return isOdd(n-1); }` routine... Maybe [see this answer](http://stackoverflow.com/questions/6370867/variadic-typedefs-or-bimaps-done-the-c0x-way/6374723#6374723) for packing arguments in a tuple for transport. – Kerrek SB Jun 25 '11 at 11:05
  • @Kerrek: To use that you also need an `isOdd(n) { return isEven(n - 1); }`, right? :-) – James McNellis Jun 26 '11 at 07:09
  • @James: Yes, that was the "exercise for the reader" part :-) – Kerrek SB Jun 26 '11 at 19:13

4 Answers4

3

Though the code got a little lengthy, I suppose the mechanism doesn't have unnecessary peculiarities.
If I understand the question correctly, probably the following code will meet the purpose:

// push front for tuple
template< class, class > struct PFT;

template< class A, class... T > struct PFT< A, tuple< T... > > {
  typedef tuple< A, T... > type;
};

// for even
template< class... > struct even_tuple;

template< class A, class B > struct even_tuple< A, B > {
  typedef tuple< A > type;
};
template< class A, class B, class... T > struct even_tuple< A, B, T... > {
  typedef typename PFT< A, typename even_tuple< T... >::type >::type type;
};
// As for odd elements, in the same way as even(please see the test on ideone)

// objective type
template< class > struct storage_type;

template< class... T > struct storage_type< tuple< T... > > {
  typedef tuple< vector< T >... > type;
};

template< class... T >
struct MyClass {
  typename storage_type< typename even_tuple< T... >::type >::type
    m_storage_even_;
  typename storage_type< typename  odd_tuple< T... >::type >::type
    m_storage_odd_;
};

Here is a test on ideone.

Ise Wisteria
  • 11,259
  • 2
  • 43
  • 26
2

Perhaps something like this:

#include <tuple>

// Example receptacle    
template <typename ...Args> struct MyContainer;

// Tuple concatenator
template<typename PackR, typename PackL> struct cat;
template<typename ...R, typename ...L>
struct cat<std::tuple<R...>, std::tuple<L...>>
{
  typedef std::tuple<R..., L...> type;
};

// Even/Odd extractors
template <typename ...Args> struct GetEven;
template <typename ...Args> struct GetOdd;

template <typename E1, typename O1, typename ...Args>
struct GetEven<E1, O1, Args...>
{
  typedef typename cat<std::tuple<E1>, typename GetEven<Args...>::value>::type value;
};
template <typename E1, typename O1>
struct GetEven<E1, O1>
{
  typedef std::tuple<E1> value;
};

template <typename E1, typename O1, typename ...Args>
struct GetOdd<E1, O1, Args...>
{
  typedef typename cat<std::tuple<O1>, typename GetEven<Args...>::value>::type value;
};
template <typename E1, typename O1>
struct GetOdd<E1, O1>
{
  typedef std::tuple<O1> value;
};

// Tuple-to-Receptacle mover
template <typename Pack, template <typename ...T> class Receiver> struct Unpack;
template <typename ...Args, template <typename ...T> class Receiver>
struct Unpack<std::tuple<Args...>, Receiver>
{
  typedef Receiver<Args...> type;
};

// Example consumer
template <typename ...Args>
struct Foo
{
  typedef typename Unpack<typename GetEven<Args...>::value, MyContainer>::type EvenVector;
  typedef typename Unpack<typename GetOdd<Args...>::value, MyContainer>::type OddVector;

  EvenVector x;
  OddVector y;
};

You still have to define your MyContainer class to do something useful with the variadic parameters, e.g. implement your tuple of vectors... (why not a vector of tuples, though?)

Credits to brunocodutra for the tuple trick.

Community
  • 1
  • 1
Kerrek SB
  • 464,522
  • 92
  • 875
  • 1,084
  • This "almost" works. The problem with this approach is that General cases of GetEven and GetOdd declare a tuple of a type and a tuple. This can be eaisly fixed by using cat from the question you linked to. I will accept this answer as soon as its fixed. P.S. I can actually paste my fixed code here but it is you who should get the credit for answering. – Bartłomiej Siwek Jun 26 '11 at 00:50
  • @Bartłomiej: Could you explain what you need concatenated? I thought you wanted to separate out the evens and the odds? I don't mind who you accept, but I'd be happy to amend the answer to your needs. – Kerrek SB Jun 26 '11 at 19:14
  • What I mean is that the code in its current state does not work as expected. If you declare Foo say then GetEven::value will resolve to std::tuple>>. What you want/need is std::tuple and this is where you need the Cat template from the other answer. – Bartłomiej Siwek Jun 27 '11 at 15:00
  • @Bartłomiej: Oh yes, indeed. Cheers. You got the answer already, though, so I'll just keep that in mind for next time. – Kerrek SB Jun 27 '11 at 15:21
0

I know your question was originally tagged "c++11", but I figure it's worth pointing out for posterity that in C++14 you have access to make_index_sequence, and that makes the whole thing pretty simple. For filtering a tuple, I'd start with this outline: https://quuxplusone.github.io/blog/2018/07/23/metafilter/

And then we end up with something like this (Godbolt):

template<bool> struct zero_or_one {
    template<class E> using type = std::tuple<E>;
};

template<> struct zero_or_one<false> {
    template<class E> using type = std::tuple<>;
};

template<class Tuple, class = std::make_index_sequence<std::tuple_size<Tuple>::value>>
struct just_evens;

template<class... Es, size_t... Is>
struct just_evens<std::tuple<Es...>, std::index_sequence<Is...>> {
    using type = decltype(std::tuple_cat(
        std::declval<typename zero_or_one<Is % 2 == 0>::template type<Es>>()...
    ));
};

To get just_odds, you'd switch the condition from Is % 2 == 0 to Is % 2 != 0.

Example usage:

static_assert(std::is_same<
    just_evens<std::tuple<char, short, int, long, double>>::type,
    std::tuple<char, int, double>
>::value, "");
Quuxplusone
  • 23,928
  • 8
  • 94
  • 159
0

this is just a try

template<typename... T> class Myclass;

template<typename T1, typename allocT1>
class MyClass <T1, allocT1> {
  std::pair<T1, allocT1> myFirstArglist;
//and you have to do a check that allocT1::value_type is same as T1 or not
//or may be alloT1 is an allocator type or not(i'm thinking concepts, may be)
//this idea is inspired from Chris's comment
};

template<typename T1, typename allocT1, typename... T>
class Myclass<T1, allocT1, T...> {
std::pair<T1, allocT1> myFirstArglist;
Myclass<T>; //something like this
};

template<>
class Myclass<> {
//probably you would like some error message here
//when there are no types and containers
};

may be i'm not clear enough, you'd probably like to read http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2006/n2080.pdf

Also there is a good post related to design of allocator types... you would like to have a look at: C++ Design Pattern for allocator type arguments

Community
  • 1
  • 1
A. K.
  • 34,395
  • 15
  • 52
  • 89
  • 1
    Specialization syntax needs to be: `MyClass` and `MyClass`, and don't forget to stick the base template declaration somewhere. – Luc Danton Jun 25 '11 at 10:14