Impossibly fast Delegate in C++11 on MSVC++14 - c++

The Problem
This question relates to several questions about function pointers here (I will not list them), but most importantly it concerns the code posted here. The post claims the code should be standard compliant and it compiles on Apple Clang just fine.
I have not managed to compile it in VS2015, i.e. MSVC++14.
What could be the source of this error and why not with Clang?
First Error
First error repeats multiple times:
...\Delegate.hpp(329): error C2514: 'Delegate<R(A...)>::is_member_pair<<unnamed-symbol>>':
class has no constructors
And the same for is_member_pair_const. It relates to the following part of the code:
template <typename T>
static typename ::std::enable_if<
!(is_member_pair<T>{} ||
is_const_member_pair<T>{}),
R>::type
functor_stub(void* const object_ptr, A&&... args)
{
return (*static_cast<T*>(object_ptr))(::std::forward<A>(args)...);
}
Second Error
Multiple errors appear, but the first is usually most important:
...\Delegate.hpp(340): error C2059: syntax error: '<end Parse>'
...\Delegate.hpp(349): note: see reference to class template instantiation 'Delegate<R(A...)>' being compiled
Then follow a few syntax errors, unrecognizable template declaration/definition etc. The code in question:
template <typename T>
static typename ::std::enable_if<
is_member_pair<T>{} ||
is_const_member_pair<T>{},
R
>::type
functor_stub(void* const object_ptr, A&&... args)
{
return (static_cast<T*>(object_ptr)->first->*
static_cast<T*>(object_ptr)->second)(::std::forward<A>(args)...);
}
For Completeness: The entire Delegate.hpp:
Code Source and Base Article
#pragma once
#ifndef DELEGATE_HPP
#define DELEGATE_HPP
#include <cassert>
#include <memory>
#include <new>
#include <type_traits>
#include <utility>
template <typename T> class Delegate;
template<class R, class ...A>
class Delegate<R (A...)>
{
using stub_ptr_type = R (*)(void*, A&&...);
Delegate(void* const o, stub_ptr_type const m) noexcept :
object_ptr_(o),
stub_ptr_(m)
{
}
public:
Delegate() = default;
Delegate(Delegate const&) = default;
Delegate(Delegate&&) = default;
Delegate(::std::nullptr_t const) noexcept : Delegate() { }
template <class C, typename =
typename ::std::enable_if< ::std::is_class<C>{}>::type>
explicit Delegate(C const* const o) noexcept :
object_ptr_(const_cast<C*>(o))
{
}
template <class C, typename =
typename ::std::enable_if< ::std::is_class<C>{}>::type>
explicit Delegate(C const& o) noexcept :
object_ptr_(const_cast<C*>(&o))
{
}
template <class C>
Delegate(C* const object_ptr, R (C::* const method_ptr)(A...))
{
*this = from(object_ptr, method_ptr);
}
template <class C>
Delegate(C* const object_ptr, R (C::* const method_ptr)(A...) const)
{
*this = from(object_ptr, method_ptr);
}
template <class C>
Delegate(C& object, R (C::* const method_ptr)(A...))
{
*this = from(object, method_ptr);
}
template <class C>
Delegate(C const& object, R (C::* const method_ptr)(A...) const)
{
*this = from(object, method_ptr);
}
template <
typename T,
typename = typename ::std::enable_if<
!::std::is_same<Delegate, typename ::std::decay<T>::type>{}
>::type
>
Delegate(T&& f) :
store_(operator new(sizeof(typename ::std::decay<T>::type)),
functor_deleter<typename ::std::decay<T>::type>),
store_size_(sizeof(typename ::std::decay<T>::type))
{
using functor_type = typename ::std::decay<T>::type;
new (store_.get()) functor_type(::std::forward<T>(f));
object_ptr_ = store_.get();
stub_ptr_ = functor_stub<functor_type>;
deleter_ = deleter_stub<functor_type>;
}
Delegate& operator=(Delegate const&) = default;
Delegate& operator=(Delegate&&) = default;
template <class C>
Delegate& operator=(R (C::* const rhs)(A...))
{
return *this = from(static_cast<C*>(object_ptr_), rhs);
}
template <class C>
Delegate& operator=(R (C::* const rhs)(A...) const)
{
return *this = from(static_cast<C const*>(object_ptr_), rhs);
}
template <
typename T,
typename = typename ::std::enable_if<
!::std::is_same<Delegate, typename ::std::decay<T>::type>{}
>::type
>
Delegate& operator=(T&& f)
{
using functor_type = typename ::std::decay<T>::type;
if ((sizeof(functor_type) > store_size_) || !store_.unique())
{
store_.reset(operator new(sizeof(functor_type)),
functor_deleter<functor_type>);
store_size_ = sizeof(functor_type);
}
else
{
deleter_(store_.get());
}
new (store_.get()) functor_type(::std::forward<T>(f));
object_ptr_ = store_.get();
stub_ptr_ = functor_stub<functor_type>;
deleter_ = deleter_stub<functor_type>;
return *this;
}
template <R (* const function_ptr)(A...)>
static Delegate from() noexcept
{
return { nullptr, function_stub<function_ptr> };
}
template <class C, R (C::* const method_ptr)(A...)>
static Delegate from(C* const object_ptr) noexcept
{
return { object_ptr, method_stub<C, method_ptr> };
}
template <class C, R (C::* const method_ptr)(A...) const>
static Delegate from(C const* const object_ptr) noexcept
{
return { const_cast<C*>(object_ptr), const_method_stub<C, method_ptr> };
}
template <class C, R (C::* const method_ptr)(A...)>
static Delegate from(C& object) noexcept
{
return { &object, method_stub<C, method_ptr> };
}
template <class C, R (C::* const method_ptr)(A...) const>
static Delegate from(C const& object) noexcept
{
return { const_cast<C*>(&object), const_method_stub<C, method_ptr> };
}
template <typename T>
static Delegate from(T&& f)
{
return ::std::forward<T>(f);
}
static Delegate from(R (* const function_ptr)(A...))
{
return function_ptr;
}
template <class C>
using member_pair =
::std::pair<C* const, R (C::* const)(A...)>;
template <class C>
using const_member_pair =
::std::pair<C const* const, R (C::* const)(A...) const>;
template <class C>
static Delegate from(C* const object_ptr,
R (C::* const method_ptr)(A...))
{
return member_pair<C>(object_ptr, method_ptr);
}
template <class C>
static Delegate from(C const* const object_ptr,
R (C::* const method_ptr)(A...) const)
{
return const_member_pair<C>(object_ptr, method_ptr);
}
template <class C>
static Delegate from(C& object, R (C::* const method_ptr)(A...))
{
return member_pair<C>(&object, method_ptr);
}
template <class C>
static Delegate from(C const& object,
R (C::* const method_ptr)(A...) const)
{
return const_member_pair<C>(&object, method_ptr);
}
void reset() { stub_ptr_ = nullptr; store_.reset(); }
void reset_stub() noexcept { stub_ptr_ = nullptr; }
void swap(Delegate& other) noexcept { ::std::swap(*this, other); }
bool operator==(Delegate const& rhs) const noexcept
{
return (object_ptr_ == rhs.object_ptr_) && (stub_ptr_ == rhs.stub_ptr_);
}
bool operator!=(Delegate const& rhs) const noexcept
{
return !operator==(rhs);
}
bool operator<(Delegate const& rhs) const noexcept
{
return (object_ptr_ < rhs.object_ptr_) ||
((object_ptr_ == rhs.object_ptr_) && (stub_ptr_ < rhs.stub_ptr_));
}
bool operator==(::std::nullptr_t const) const noexcept
{
return !stub_ptr_;
}
bool operator!=(::std::nullptr_t const) const noexcept
{
return stub_ptr_;
}
explicit operator bool() const noexcept { return stub_ptr_; }
R operator()(A... args) const
{
// assert(stub_ptr);
return stub_ptr_(object_ptr_, ::std::forward<A>(args)...);
}
private:
friend struct ::std::hash<Delegate>;
using deleter_type = void (*)(void*);
void* object_ptr_;
stub_ptr_type stub_ptr_{};
deleter_type deleter_;
::std::shared_ptr<void> store_;
::std::size_t store_size_;
template <class T>
static void functor_deleter(void* const p)
{
static_cast<T*>(p)->~T();
operator delete(p);
}
template <class T>
static void deleter_stub(void* const p)
{
static_cast<T*>(p)->~T();
}
template <R (*function_ptr)(A...)>
static R function_stub(void* const, A&&... args)
{
return function_ptr(::std::forward<A>(args)...);
}
template <class C, R (C::*method_ptr)(A...)>
static R method_stub(void* const object_ptr, A&&... args)
{
return (static_cast<C*>(object_ptr)->*method_ptr)(
::std::forward<A>(args)...);
}
template <class C, R (C::*method_ptr)(A...) const>
static R const_method_stub(void* const object_ptr, A&&... args)
{
return (static_cast<C const*>(object_ptr)->*method_ptr)(
::std::forward<A>(args)...);
}
template <typename>
struct is_member_pair : std::false_type { };
template <class C>
struct is_member_pair< ::std::pair<C* const,
R (C::* const)(A...)> > : std::true_type
{
};
template <typename>
struct is_const_member_pair : std::false_type { };
template <class C>
struct is_const_member_pair< ::std::pair<C const* const,
R (C::* const)(A...) const> > : std::true_type
{
};
template <typename T>
static typename ::std::enable_if<
!(is_member_pair<T>{} ||
is_const_member_pair<T>{}),
R
>::type
functor_stub(void* const object_ptr, A&&... args)
{
return (*static_cast<T*>(object_ptr))(::std::forward<A>(args)...);
}
template <typename T>
static typename ::std::enable_if<
is_member_pair<T>{} ||
is_const_member_pair<T>{},
R
>::type
functor_stub(void* const object_ptr, A&&... args)
{
return (static_cast<T*>(object_ptr)->first->*
static_cast<T*>(object_ptr)->second)(::std::forward<A>(args)...);
}
};
namespace std
{
template <typename R, typename ...A>
struct hash<::Delegate<R (A...)> >
{
size_t operator()(::Delegate<R (A...)> const& d) const noexcept
{
auto const seed(hash<void*>()(d.object_ptr_));
return hash<typename ::Delegate<R (A...)>::stub_ptr_type>()(
d.stub_ptr_) + 0x9e3779b9 + (seed << 6) + (seed >> 2);
}
};
}
#endif // DELEGATE_HPP

As Igor commented, the following solves the problem: replace is_member_pair<T>{} with is_member_pair<T>::value everywhere and do the same for is_const_member_pair.

Related

Why my UniqPtr objects are double in size compared to std::unique_ptr?

In real applications I should stick to the standard library facilities, for practice and understanding how those facilities work I should try to implement my own.
Here I've implemented a simulation of the smart pointer unique_ptr:
#include<iostream>
#include <memory>
template <typename T>
class DefDel
{
public:
template <typename U>
void operator()(U* p)const
{
std::cout << "freeing memory...\n";
delete p;
}
};
template <typename T>
class DefDel<T[]>
{
public:
template <typename U>
void operator()(U* p)const
{
std::cout << "freeing memory of an array of objects...\n";
delete[] p;
}
};
template <typename T, typename D = DefDel<T>>
class UniqPtr final
{
public:
UniqPtr(T* = nullptr, D = DefDel<T>{});
UniqPtr(UniqPtr const&) = delete;
UniqPtr(UniqPtr&&) noexcept;
UniqPtr& operator =(UniqPtr const&) = delete;
UniqPtr& operator =(UniqPtr&&) noexcept;
~UniqPtr();
T& operator*();
T const& operator*() const;
T* operator->();
T const* operator->() const;
operator bool() const;
private:
T* ptr_{nullptr};
D del_{};
};
template <typename T, typename D>
UniqPtr<T, D>::UniqPtr(T* p, D del) :
ptr_(p),
del_(del)
{}
template <typename T, typename D>
UniqPtr<T, D>::UniqPtr(UniqPtr&& rhs) noexcept :
ptr_(std::move(rhs.ptr_)),
del_(std::move(rhs.del_))
{
rhs.ptr_ = nullptr;
}
template <typename T, typename D>
UniqPtr<T, D>& UniqPtr<T, D>::operator = (UniqPtr&& rhs) noexcept
{
if(this != &rhs)
{
ptr_ = std::move(rhs.ptr_);
del_ = std::move(rhs.del_);
rhs.ptr_ = nullptr;
}
return *this;
}
template <typename T, typename D>
UniqPtr<T, D>::~UniqPtr()
{
del_(ptr_);
}
template <typename T, typename D>
T& UniqPtr<T, D>::operator*()
{
return *ptr_;
}
template <typename T, typename D>
T const& UniqPtr<T, D>::operator*() const
{
return *ptr_;
}
template <typename T, typename D>
T* UniqPtr<T, D>::operator->()
{
return ptr_;
}
template <typename T, typename D>
T const* UniqPtr<T, D>::operator->() const
{
return ptr_;
}
template <typename T, typename D>
UniqPtr<T, D>::operator bool() const
{
return ptr_;
}
// for array
template <typename T, typename D>
class UniqPtr<T[], D> final
{
public:
UniqPtr(T* = nullptr, D = DefDel<T[]>{});
UniqPtr(UniqPtr const&) = delete;
UniqPtr(UniqPtr&&) noexcept;
UniqPtr& operator =(UniqPtr const&) = delete;
UniqPtr& operator =(UniqPtr&&) noexcept;
~UniqPtr();
T& operator*();
T const& operator*() const;
T* operator->();
T const* operator->() const;
operator bool() const;
private:
T* ptr_{nullptr};
D del_{};
};
template <typename T, typename D>
UniqPtr<T[], D>::UniqPtr(T* p, D del) :
ptr_(p),
del_(del)
{}
template <typename T, typename D>
UniqPtr<T[], D>::UniqPtr(UniqPtr&& rhs) noexcept :
ptr_(std::move(rhs.ptr_)),
del_(std::move(rhs.del_))
{
rhs.ptr_ = nullptr;
}
template <typename T, typename D>
UniqPtr<T[], D>& UniqPtr<T[], D>::operator = (UniqPtr&& rhs) noexcept
{
if(this != &rhs)
{
ptr_ = std::move(rhs.ptr_);
del_ = std::move(rhs.del_);
rhs.ptr_ = nullptr;
}
return *this;
}
template <typename T, typename D>
UniqPtr<T[], D>::~UniqPtr()
{
del_(ptr_);
}
template <typename T, typename D>
T& UniqPtr<T[], D>::operator*()
{
return *ptr_;
}
template <typename T, typename D>
T const& UniqPtr<T[], D>::operator*() const
{
return *ptr_;
}
template <typename T, typename D>
T* UniqPtr<T[], D>::operator->()
{
return ptr_;
}
template <typename T, typename D>
T const* UniqPtr<T[], D>::operator->() const
{
return ptr_;
}
template <typename T, typename D>
UniqPtr<T[], D>::operator bool() const
{
return ptr_;
}
int main()
{
UniqPtr<int[]> upi(new int[3]{57});
std::cout << sizeof(upi) << '\n';
std::unique_ptr<int[], DefDel<int[]>> upi2(new int[3]{57});
std::cout << sizeof(upi2) << '\n';
}
Why the size of my UniqPtr objects are double in size as std::unique_ptr (even being initialized with the same values)?
Is that because of my class is storing a Del_ object as a member?
If that is the problem then how could I achieve the very similar behavior as unique_ptr with 0 cost?
Why the size of my UniqPtr objects are double in size as std::unique_ptr (even being initialized with the same values)?
Is that because of my class is storing a Del_ object as a member?
Yes.
Because D del_ needs to have storage, and every T* ptr_ has to be properly aligned
If that is the problem then how could I achieve the very similar behavior as unique_ptr with 0 cost?
You can privately derive from D, rather than have it be a member. Then an instantiation with an empty class may cave it occupy no extra storage.
template <typename T, typename D = DefDel<T>>
class UniqPtr final : D
{
public:
UniqPtr(T* = nullptr, D = {});
UniqPtr(UniqPtr const&) = delete;
UniqPtr(UniqPtr&&) noexcept;
UniqPtr& operator =(UniqPtr const&) = delete;
UniqPtr& operator =(UniqPtr&&) noexcept;
~UniqPtr();
T& operator*();
T const& operator*() const;
T* operator->();
T const* operator->() const;
operator bool() const;
private:
T* ptr_{nullptr};
};
template <typename T, typename D>
UniqPtr<T, D>::UniqPtr(T* p, D del) :
D(del),
ptr_(p)
{}
template <typename T, typename D>
UniqPtr<T, D>::UniqPtr(UniqPtr&& rhs) noexcept :
D(std::move(*rhs)),
ptr_(std::exchange(rhs.ptr_, nullptr))
{}
template <typename T, typename D>
UniqPtr<T, D>& UniqPtr<T, D>::operator = (UniqPtr&& rhs) noexcept
{
using std::swap;
swap(static_cast<D&>(*this), static_cast<D&>(rhs));
swap(ptr_, rhs.ptr_);
}
template <typename T, typename D>
UniqPtr<T, D>::~UniqPtr()
{
static_cast<D&>(*this)(ptr_);
}

how to avoid many similar overloads for C strings

Here is the code:
template <typename L, typename R> bool eq (const L& lhs, const R& rhs) { return lhs == rhs; }
template<int N> bool eq(char* lhs, const char(&rhs)[N]) { return String(lhs).compare(rhs) == 0; }
template<int N> bool eq(const char(&lhs)[N], char* rhs) { return String(lhs).compare(rhs) == 0; }
inline bool eq(char* lhs, char* rhs) { return String(lhs).compare(rhs) == 0; }
inline bool eq(const char* lhs, const char* rhs) { return String(lhs).compare(rhs) == 0; }
inline bool eq(char* lhs, const char* rhs) { return String(lhs).compare(rhs) == 0; }
inline bool eq(const char* lhs, char* rhs) { return String(lhs).compare(rhs) == 0; }
I have to do this for neq/lt/gt/lte/gte and not just for equality. Maybe I've already missed something.
Is there a way to not list all the possible combinations of C string types?
Also C++98.
EDIT: >> here << is an online demo with the problem
Decay an array type to pointer:
template<class T>
struct decay_array { typedef T type; };
template<class T, size_t N>
struct decay_array<T[N]> { typedef T* type; };
template<class T>
struct decay_array<T[]> { typedef T* type; };
Check that a type is not a pointer to (possibly const) char:
template<class T>
struct not_char_pointer { enum { value = true }; };
template<>
struct not_char_pointer<char*> { enum { value = false }; };
template<>
struct not_char_pointer<const char*> { enum { value = false }; };
Now check that a type is not a pointer to or array of (possibly const) char:
template<class T>
struct can_use_op : not_char_pointer<typename decay_array<T>::type> {};
Reimplement std::enable_if:
template<bool, class = void>
struct enable_if {};
template<class T>
struct enable_if<true, T> { typedef T type; };
and use it to constrain your template:
template <typename L, typename R>
typename enable_if<can_use_op<L>::value || can_use_op<R>::value, bool>::type
eq (const L& lhs, const R& rhs) { return lhs == rhs; }
Then just one overload is enough:
inline bool eq(const char* lhs, const char* rhs) { return String(lhs).compare(rhs) == 0; }
namespace details{
template<template<class...>class Z,class,class...Ts>
struct can_apply:std::false_type{};
template<template<class...>class Z,class...Ts>
struct can_apply<Z,std::void_t<Z<Ts...>>,Ts...>:std::true_type{};
}
template<template<class...>class Z,class...Ts>
using can_apply=details::can_apply<Z,void,Ts...>;
This tests if a template can be applied some types.
namespace strcmp{
bool eq(const char*lhs, const char*rhs){/* body */}
}
template<class L, class R>
using str_eq_r=decltype(strcmp::eq(std::declval<L>(),std::declval<R>()));
template<class L, class R>
using can_str_eq=can_apply<str_eq_r,L,R>;
can_str_eq is truthy iff we can call stdcmp::eq on it.
namespace details {
bool eq(const char* lhs, const char* rhs, std::true_type){
return strcmp::eq(lhs,rhs);
}
template<class L,class R>
bool eq(L const& l, R const&r,std::false_type){
return l==r;
}
}
template<class L,class R>
bool eq(L const& l, R const&r){
return details::eq(l,r,can_str_eq<L const&,R const&>{});;
}
We could also use a static_if trick to do it inline, if you like:
template<class L,class R>
bool eq(L const& l, R const&r){
return static_if<can_str_eq>( l, r )(
strcmp::eq,
[](auto&& l, auto&& r){return l==r;}
);
}
After writing a static_if:
template<class...Ts>
auto functor(Ts...ts){
return [=](auto&& f){
return f(ts...);
};
}
namespace details{
template<class Functor>
auto switcher(std::true_type, Functor functor){
return [=](auto&& t, auto&&){
return functor(t);
};
}
template<class Functor>
auto switcher(std::false_type, Functor functor){
return [=](auto&&, auto&& f){
return functor(f);
};
}
}
template<template<class...>class test, class...Ts>
auto static_if(Ts...ts){
return details::switcher(
test<Ts...>{},
functor(ts...)
);
}
now, what are the odds that works? (Written on phone, not compiled yet) Also not optimal: lots of perfect forwarding, some of which requires de-lamdaing, required.

operator== container iterator const and non-const

I'm trying to have an eventmanager for the impossibly fast c++ delegate that was discussed in this article http://blog.coldflake.com/posts/C++-delegates-on-steroids/;
template <typename T, typename R, typename... Params>
class delegate<R (T::*)(Params...) const>
{
public:
typedef R (T::*func_type)(Params...) const;
delegate(func_type func, const T& callee)
: callee_(callee) , func_(func)
{}
R operator()(Params... args) const
{ return (callee_.*func_)(args...); }
bool operator==(const delegate& other) const
{ return (&callee_ == &other.callee_) && (func_ == other.func_); }
bool operator!= (const delegate& other) const
{ return !(*this == other); }
private:
const T& callee_;
func_type func_;
};
and one of the methods that I used;
using EventListenerDelegate = delegate<IEventDataPtr>;
using EventListenerList = std::list<EventListenerDelegate>;
bool EventManager::addListener(const EventListenerDelegate& eventDelegate, const EventType& type)
{
// this will find or create the entry
EventListenerList& eventListenerList = m_eventListeners[type];
for (auto it = eventListenerList.begin(); it != eventListenerList.end(); ++it)
{
if (eventDelegate == (*it))
{
std::cout <<"Attempting to double-register a delegate" << std::endl;
return false;
}
}
eventListenerList.push_back(eventDelegate);
return true;
}
The code failed to compile with gcc error message;
error: no match for 'operator==' (operand types are 'const EventListenerDelegate {aka const delegate<std::shared_ptr<IEventData> >}' and 'delegate<std::shared_ptr<IEventData> >')
if (eventDelegate == (*it))
^
What should I do to fix the error?
Thank you.
EDIT:
two other specialization templates in the code;
//specialization for free functions
template <typename R, typename... Params>
class delegate<R (*)(Params...)>
and;
//specialization for member functions
template <typename T, typename R, typename... Params>
class delegate<R (T::*)(Params...)>

Tag dispatching/enable_if - I am confused

I have the following structs:
struct A
{
}
struct B
{
tuple<string,string> children{{"test1","test2"}};
}
I would like to create a template function that will overload the << operator on every class having a member variable called children.
If possible- only on tuples named children.
When a class with a children tuple like B is met it should iterate the tuple members and call << on each of them.
Something like:
template<typename RECEIVERTYPE,typename SENDERTYPE>
typename std::enable_if<std::have_children_member<RECEIVER_TYPE>::value, void>::type
RECEIVERTYPE& operator<< (RECEIVERTYPE& streamReceiver, const SENDERTYPE& streamSender)
{
for_each(streamSender.children, [&](const auto& child)
{
streamReceiver << child;
});
return streamReceiver;
}
I have tried alot of examples- but I cant really get anything working in visual studio 2015.
I would just do this to only match types with a tuple member called children:
template<typename S, typename T,
std::size_t = std::tuple_size<decltype(T::children)>::value>
S& operator<<(S& s, const T& t)
{ ... }
And might implement the body like this:
template<typename S, typename T, std::size_t... I>
void
print_tuple_like(S& s, const T& t, std::index_sequence<I...>)
{
void* unused[] = { &(s << std::get<I>(t))... };
}
template<typename S, typename T,
std::size_t N = std::tuple_size<decltype(T::children)>::value>
S& operator<<(S& s, const T& t)
{
print_tuple_like(s, t.children, std::make_index_sequence<N>{});
return s;
}
Or like:
template<std::size_t N, typename S, typename T>
void
print_tuple_like(S& s, const T& t, std::false_type)
{ }
template<std::size_t N, typename S, typename T>
void
print_tuple_like(S& s, const T& t, std::true_type)
{
s << std::get<N>(t);
print_tuple_like<N+1>(s, t, std::integral_constant<bool, (N+1 < std::tuple_size<T>::value)>{});
}
template<typename S, typename T,
std::size_t N = std::tuple_size<decltype(T::children)>::value>
S& operator<<(S& s, const T& t)
{
print_tuple_like<0>(s, t.children, std::integral_constant<bool, (N != 0)>{});
return s;
}

Apply a tuple of functions to a value and return a tuple

Right now, I have the following to apply two functions to a value and return a 2-value tuple:
template<typename F1, typename F2>
class Apply2
{
public:
using return_type = std::tuple<typename F1::return_type, typename F2::return_type>;
Apply2(const F1& f1, const F2& f2) : f1_(f1), f2_(f2) {}
template<typename T> return_type operator()(const T& t) const
{
return std::make_tuple(f1_(t), f2_(t));
}
protected:
const F1& f1_;
const F2& f2_;
};
I wanted to generalize this to N functions:
template<typename ...F>
class ApplyN
{
public:
using return_type = std::tuple<typename F::return_type...>;
ApplyN(const std::tuple<F...>& fs) : functions_(fs) {}
template<typename T> return_type operator()(const T& t) const
{
return ???;
}
protected:
std::tuple<F...> functions_;
};
I know I probably need to use template recursion somehow, but I can't wrap my head around it. Any ideas?
It took me a while, but here it is (using indices):
template<typename ...F>
class ApplyN
{
public:
using return_type = std::tuple<typename F::return_type...>;
ApplyN(const F&... fs) : functions_{fs...} {}
template<typename T> return_type operator()(const T& t) const
{
return with_indices(t, IndicesFor<std::tuple<F...> >{});
}
protected:
std::tuple<F...> functions_;
template <typename T, std::size_t... Indices>
return_type with_indices(const T& t, indices<Indices...>) const
{
return return_type{std::get<Indices>(functions_)(t)...};
}
};
Someone had an (incomplete) answer before, but s/he erased it - that was my starting point. Anyway, thank you stranger! Thank you R. Martinho Fernandes too!