Using Declaration in Variadic Template

using declaration in variadic template

Ok i found out a pretty decent solution:

basically i need to unpack one extra lambda case and apply the using clause to the unpacked lambda and the rest, but in this case, since i apparently i cannot make a variadic list of using declarations (at least i don't know the syntax, if its possible), the rest is wrapped by inheriting from the 'rest' case, like this:

template <typename ReturnType, typename... Lambdas>
struct lambda_visitor;

template <typename ReturnType, typename Lambda1, typename... Lambdas>
struct lambda_visitor< ReturnType, Lambda1 , Lambdas...>
: public lambda_visitor<ReturnType, Lambdas...>, public Lambda1 {

using Lambda1::operator();
using lambda_visitor< ReturnType , Lambdas...>::operator();
lambda_visitor(Lambda1 l1, Lambdas... lambdas)
: Lambda1(l1), lambda_visitor< ReturnType , Lambdas...> (lambdas...)
{}
};

template <typename ReturnType, typename Lambda1>
struct lambda_visitor<ReturnType, Lambda1>
: public boost::static_visitor<ReturnType>, public Lambda1 {

using Lambda1::operator();
lambda_visitor(Lambda1 l1)
: boost::static_visitor<ReturnType>(), Lambda1(l1)
{}
};

template <typename ReturnType>
struct lambda_visitor<ReturnType>
: public boost::static_visitor<ReturnType> {

lambda_visitor() : boost::static_visitor<ReturnType>() {}
};

So i can do this inductively by placing two using declarations, one from the unpacked lambda type and another from the parent class, which is actually the same class with one less lambda.

using declaration of a specialized variadic template class

No, that is impossible. From the standard §14.3/4, emphasis mine:

When template argument packs or default template-arguments are used, a template-argument list can be
empty. In that case the empty <> brackets shall still be used as the template-argument-list. [ Example:

  template <class T = char> class String;
String<>* p; // OK: String<char>
String* q; // syntax error

template <class ... Elements> class Tuple;
Tuple<>* t; // OK: Elements is empty
Tuple* u; // syntax error

—end example ]

But then, what's wrong with writing FloatType<>? If seeing the empty brackes really irks you, you can introduce another alias for them, but that kind of obfuscates things:

using DefFloatType = FloatType<>;

Plus, it's more typing!

variadic template with recursive inheritance and using declaration

With multiple inheritance and an additional using directive:

template <typename First, typename... Rest>
struct Base : Base<First>, Base<Rest...> {
using Base<First>::func;
using Base<Rest...>::func;
};

template <typename First>
struct Base<First> {
void func(First object) {/*...*/}
};

Live Demo (C++11)

Variadic template declaration from single integer argument?

You can write a meta-function that accepts N, base_t, and SomeOtherClass, and recursively calls itself with a smaller N, each time tacking on base_t to the end of a growing parameter pack:

template <int N, typename T, template<typename...> typename C, typename ...Ts>
struct expand {
using type = typename expand<N-1, T, C, T, Ts...>::type;
};

For the base case, when N goes down to 0, the meta-function yields SomeOtherClass instantiated with the parameter pack of N base_t types:

template <typename T, template<typename...> typename C, typename ...Ts>
struct expand<0, T, C, Ts...> {
using type = C<Ts...>;
};

Also, convenience alias is nice to avoid having to say typename at the call site:

template <int N, typename T, template<typename...> typename C>
using expand_t = typename expand<N, T, C>::type;

Now at the call site you can write:

expand_t<N, base_t, SomeOtherClass> foo; 
// equivalent to
// SomeOtherClass< base_t, base_t, ..., base_t > foo;
// ^^^ N times ^^^

Here's a demo.

Expand non-type template parameter pack with using declaration (template variadic compile-time SignalSlot implementation)

Finally I came up with the solution, it's usage is pretty simple, as i wanted.

Here is my working example!

#include <tuple>
#include <iostream>

template <class ...>
struct TW {};

template <class ClType, class ... ArgTypes>
constexpr ClType ClassType(void(ClType::*)(ArgTypes...));

template <class ClType, class ... ArgTypes>
constexpr TW<ArgTypes...> ArgsType(void(ClType::*)(ArgTypes...));

template <auto pFunc>
using class_trait = decltype(ClassType(pFunc));

template <auto pFunc>
using args_trait = decltype(ArgsType(pFunc));

template <class, class>
struct _func_trait;

template <class ClType, template <class...> class Pack, class ... ArgTypes>
struct _func_trait<ClType, Pack<ArgTypes...>>
{
typedef void(ClType::*FuncPtr)(ArgTypes...);
typedef ClType ClassType;
typedef Pack<ArgTypes...> Args;
};

template <auto pFunc>
struct func_traits : public _func_trait<class_trait<pFunc>, args_trait<pFunc>>
{};

template <auto L, class Pack = args_trait<L>>
struct ClassImpl;

template <auto L, template <class ...> class Pack, class ... ArgTypes>
struct ClassImpl<L, Pack<ArgTypes...>>
{
void invoke(decltype(L), ArgTypes ... args)
{
(std::cout << ... << args) << std::endl;
}
};

template <class ... Impls>
struct ISignalMap : protected Impls...
{
using Impls::invoke...;
};

template <auto ... L>
struct SignalsMap
{
//just to see the pointers' values
static constexpr std::tuple<decltype(L)...> t{ std::make_tuple(L...) };
ISignalMap<ClassImpl<L>...> Signals{};
};

struct Signals
{
void func1(int a, double b) {}
void func12(int a, double b) {}

void func2(double a, double b, int c) {}

constexpr void func3(const char*) {}
};

int main(void)
{
auto& ref = SignalsMap<&Signals::func1, &Signals::func2, &Signals::func3>::t;

//add SignalsMap as member to your class and pass the pointers to
//methods you need to be signals
SignalsMap<&Signals::func1, &Signals::func2, &Signals::func3> sm;

//first parameter is a pointer to a signal you want to invoke
sm.Signals.invoke(&Signals::func2, 4.8, 15.16, 23);
sm.Signals.invoke(&Signals::func1, 23, 42.108);
sm.Signals.invoke(&Signals::func12, 23, 42.108);

sm.Signals.invoke(&Signals::func3, "Eat this!");

return 0;
}

Declare member variables from variadic template parameter

You could use a std::tuple

#include <tuple>

template <typename... Types> class Data
{
std::tuple<Types...> items;
};

struct Item1
{
int a;
};

struct Item2
{
float x, y, z;
};

struct Item3
{
std::string name;
};

int main()
{
Data<Item1, Item2> data1;
Data<Item3> data2;
}

Try it here

Can the using declaration be used with templates?

What you linked to is a using directive. A using declaration can be used fine with templated base classes (haven't looked it up in the standard, but just tested it with a compiler):

template<typename T> struct c1 { 
void foo() { std::cout << "empty" << std::endl; }
};

template<typename T> struct c2 : c1<T> {
using c1<T>::foo;
void foo(int) { std::cout << "int" << std::endl; }
};

int main() {
c2<void> c;
c.foo();
c.foo(10);
}

The compiler correctly finds the parameter-less foo function because of our using-declaration re-declaring it into the scope of c2, and outputs the expected result.

Edit: updated the question. here is the updated answer:

The article is right about that you are not allowed to use a template-id (template name and arguments). But you can put a template name:

struct c1 { 
template<int> void foo() { std::cout << "empty" << std::endl; }
};

struct c2 : c1 {
using c1::foo; // using c1::foo<10> is not valid
void foo(int) { std::cout << "int" << std::endl; }
};

int main() {
c2 c;
c.foo<10>();
c.foo(10);
}

How to use using keyword for variadic template

If I understand correctly your question (?), you can define a type-traits as

template <typename ... Types>
struct oTypes
{ using type = OutputType<Types...>; };

template <typename ... Types>
struct oTypes<std::tuple<Types...>>
{ using type = OutputType<Types...>; };

and then define dummy as follows

template <typename F>
struct dummy
{ using output = typename oTypes<typename F::output>::type; };

The following is a full compilable example

#include <tuple>
#include <utility>

struct f1
{
using output = double;

output operator() ()
{ return 0.0; }
};

struct f2
{
using output = std::tuple<double,int>;

output operator() ()
{ return { 1.0, 2 }; }
};

template <typename ... Types>
class OutputType
{
private:
std::tuple<Types...> m_val;

public:
OutputType(std::tuple<Types...>&& val) : m_val(val)
{ }

OutputType(Types&& ... val) : m_val(std::forward<Types>(val)...)
{ }
};

template <typename ... Types>
struct oTypes
{ using type = OutputType<Types...>; };

template <typename ... Types>
struct oTypes<std::tuple<Types...>>
{ using type = OutputType<Types...>; };

template <typename F>
struct dummy
{ using output = typename oTypes<typename F::output>::type; };

int main()
{
static_assert( std::is_same<dummy<f1>::output,
OutputType<double>>::value, "!");
static_assert( std::is_same<dummy<f2>::output,
OutputType<double, int>>::value, "!!");
}


Related Topics



Leave a reply



Submit