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.

Upvotes: 1

Views: 342

Answers (2)

max66
max66

Reputation: 66200

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.

Upvotes: 3

Klaus
Klaus

Reputation: 25593

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

Upvotes: 1

Related Questions