I like using factory design pattern to inject dependencies, but that means having lots of very similar classes (almost one factory per class). Then I got to an idea to create a generic factory pattern using templates. Something like this:
// g++ -std=c++14 -Wall -Wextra factory.cpp -o factory
#include <functional>
#include <memory>
#include <utility>
#include <iostream>
template < typename T, typename Base = typename T::Iface >
class Factory
{
public:
template < typename... Args >
using CreatorFn = std::function< std::shared_ptr< Base > ( Args&&... ) >;
template < typename... Args >
static std::shared_ptr< Base > Create( Args&&... args );
template < typename... Args >
static void ResetToDefaultCreator();
template < typename... Args >
static void SetCreator( CreatorFn< Args... > fn );
private:
Factory() = delete;
template < typename... Args >
static CreatorFn< Args... >& Creator();
template < typename... Args >
static std::shared_ptr< Base > DefaultCreator( Args&&... args );
};
template < typename T, typename Base >
template < typename... Args >
std::shared_ptr< Base > Factory< T, Base >::Create( Args&&... args )
{
return Creator< Args... >()( std::forward< Args >( args )... );
}
template < typename T, typename Base >
template < typename... Args >
void Factory< T, Base >::ResetToDefaultCreator()
{
CreatorFn< Args... >() = DefaultCreator< Args... >;
}
template < typename T, typename Base >
template < typename... Args >
void Factory< T, Base >::SetCreator( CreatorFn< Args... > fn )
{
Creator< Args... >() = fn;
}
template < typename T, typename Base >
template < typename... Args >
Factory< T, Base >::CreatorFn< Args... >& Factory< T, Base >::Creator()
{
static CreatorFn< Args... > creator = DefaultCreator< Args... >;
return creator;
}
template < typename T, typename Base >
template < typename... Args >
std::shared_ptr< Base > Factory< T, Base >::DefaultCreator( Args&&... args )
{
return std::make_shared< T >( std::forward< Args >( args )... );
}
struct A {
virtual ~A() = default;
virtual void foo() = 0;
};
struct B : public A {
using Iface = A;
virtual void foo()
{
std::cout << "-- B::foo()" << std::endl;
}
};
struct C : public A {
using Iface = A;
C( int, float )
{
}
virtual void foo()
{
std::cout << "-- C::foo()" << std::endl;
}
};
struct D : public A {
using Iface = A;
D( int, float )
{
}
virtual void foo()
{
std::cout << "-- D::foo()" << std::endl;
}
};
using FactoryDefaultConstructor = Factory< B >;
using FactoryParamsConstructor = Factory< C >;
struct MyClass
{
MyClass() : a( FactoryParamsConstructor::Create( 3, 5.7f ) )
{}
void foo()
{
a->foo();
}
private:
std::shared_ptr< A > a;
};
int main()
{
FactoryParamsConstructor::ResetToDefaultCreator<int,float>();
std::shared_ptr< A > obj1 = FactoryParamsConstructor::Create( 3, 5 );
C* realObj1 = dynamic_cast< C* >( obj1.get() );
if ( nullptr != realObj1 )
{
std::cout << "1 created" << std::endl;
}
else
{
std::cout << "1 failed" << std::endl;
}
MyClass class1;
class1.foo();
FactoryParamsConstructor::CreatorFn< int, float > newCretorFn = []( int a,float b ){
std::cout << "****cb called"<<std::endl;
return std::shared_ptr< A >( new D( a, b ) );
};
FactoryParamsConstructor::SetCreator< int, float >( newCretorFn );
std::shared_ptr< A > obj2 = FactoryParamsConstructor::Create( 3, 5.7f );
D* realObj2 = dynamic_cast< D* >( obj2.get() );
if ( nullptr != realObj2 )
{
std::cout << "2 created" << std::endl;
}
else
{
std::cout << "2 failed" << std::endl;
}
float p = 5.5f;
std::shared_ptr< A > obj3 = FactoryParamsConstructor::Create( 3, p );
D* realObj3 = dynamic_cast< D* >( obj3.get() );
if ( nullptr != realObj3 )
{
std::cout << "3 created" << std::endl;
}
else
{
std::cout << "3 failed" << std::endl;
}
MyClass class2;
class2.foo();
}
Output:
1 created
-- C::foo()
****cb called
2 created
3 failed
****cb called
-- D::foo()
This works, but with certain problems:
if I don't take care what I pass to the Create() method, it can fail, since it is going to use wrong instance of the Creator() method. Is there a way to fix this? This is the reason why creation of obj3 fails.
The SetCreator() method can take only std::function objects. I understand why. My question is, can I change it to take anything appropriate and call correct Creator() method?
Ideally, it would have this declaration:
template < typename F >
static void SetCreator( F fn );
Then I could do this:
std::shared_ptr< A > foo( int, float ) { return new B; };
FactoryParamsConstructor::SetCreator( foo );
Maybe overloaded is what you want (C++17, but might be implemented for C++11):
template<class... Ts> struct overloaded : Ts... { using Ts::operator()...; };
template<class... Ts> overloaded(Ts...) -> overloaded<Ts...>;
const auto Afactory = overloaded {
[]() { return std::make_shared<B>(); },
[](int a, int b) { return std::make_shared<C>(a, b); },
[](int a, float b) { return std::make_shared<D>(a, b); },
};
Demo
I figured out how to do, and posting my solution.
template < typename T, typename Base = typename T::Iface >
class Factory
{
public:
using ReturnType = std::shared_ptr< Base >;
template < typename... Args >
using CreatorFn = std::function< ReturnType ( Args... ) >;
template < typename... Args >
static ReturnType Create( Args... args )
{
return Creator< decltype( args )... >()( args... );
}
template < typename... Args >
static void ResetToDefaultCreator()
{
Creator< Args... >() = &DefaultCreator< Args... >;
}
template < typename... Args >
static void SetCreator( CreatorFn< Args... > fn )
{
Creator< Args... >() = fn;
}
private:
Factory() = delete;
template < typename... Args >
static CreatorFn< Args... >& Creator()
{
static_assert( ( std::is_same< Args, std::decay_t< Args > >::value && ... ), "None of creator arguments can have a reference.");
static CreatorFn< Args... > creator = &DefaultCreator< Args... >;
return creator;
}
template < typename... Args >
static ReturnType DefaultCreator( Args... args )
{
return std::make_shared< T >( args... );
}
};
Related
I have following Event class, inside of which there is wrapper to register some callable (lambda, std::function, etc) as callback:
#include <boost/type_traits/function_traits.hpp>
template< typename ...Args >
class Callback
{
public:
virtual void OnEvent( const Args&... args ) = 0;
virtual void OnEvent( const EmitContext&, const Args&... args )
{
OnEvent( args... );
}
};
template< typename ...Args >
class Event
{
template< typename Callable >
class CallbackImpl: public Callback< Args... >
{
public:
CallbackImpl( Callable&& func ) : mFunc( func )
{
}
void OnEvent( const Args&... args ) override
{
Call( mFunc, args... );
}
void OnEvent( const EmitContext& emit_context, const Args&... args ) override
{
if constexpr( boost::function_traits< std::remove_pointer < Function > >::arity && std::is_same_v< boost::function_traits< std::remove_pointer< Function > >::arg1_type, EmitContext > )
Call( mFunc, emit_context, args... );
else
OnEvent( args... );
}
private:
using Function = std::remove_reference_t<Callable >;
Function mFunc;
};
template < typename Callable >
void RegisterCallbackFunction( Callable&& func )
{
mCallback = std::make_shared< CallbackImpl< Callable > >( std::forward< Callable >( func ) );
}
void Emit( const Args...& args )
{
EmitContext ctx;
if( mCallback )
mCallback->OnEvent( ctx, args... );
}
private:
std::shared_ptr< Callback< Args... > > mCallback;
};
Callable can accept N first arguments from Args, where 0 <= N <= sizeof( Args... ), Call function deducts appropriate invoke.
Now I need to add variant, where Callable can accept either N arguments as before or some EmitContext structure and N arguments after it.
I'm trying to get arity and 1st argument type (if there is) using boost::function traits, but I get compilation errors:
error: implicit instantiation of undefined template 'boost::detail::function_traits_helper<(lambda at collection_event_manager.cpp:71:57) *>'
public boost::detail::function_traits_helper<typename boost::add_pointer<Function>::type>
error: incomplete definition of type 'boost::function_traits<(lambda at collection_event_manager.cpp:71:57)>'
What am I doing wrong? I've also tried to use it without std::remove_pointer and with std::remove_pointer_t with no avail.
From the docs:
function_traits is intended to introspect only C++ functions of the form R (), R( A1 ), R ( A1, ... etc. )
You want to "register some callable (lambda, std::function, etc) as callback". Lambdas are not guaranteed to have implicit conversions to C++ function pointers (though they might), std::function is certainly never compatible.
What I'd usually do instead of this hard-wrangling of signatures is more like duck-typing:
if constexpr(std::is_invocable_v<Callable, Args...>)
Call(mFunc, emit_context, args...);
else
OnEvent(args...);
This just asks the compiler whether it call with emit_context compiles. I know that this is slightly different, and it may not work as expected if your callable is e.g. [](auto&&...){}, but then again it might be just what you wanted.
Here's a simplified take that also removes the need for the template method:
Live On Coliru
#include <functional>
#include <iostream>
#include <memory>
#include <type_traits>
struct EmitContext {
int some_value;
};
template <typename... Args> class Event {
public:
template <typename F> void RegisterCallbackFunction(F&& func) {
impl_ = std::make_shared<Wrap<std::decay_t<F>>>(std::forward<F>(func));
}
void Emit(const Args&... args) const {
EmitContext ctx{123};
if (impl_)
impl_->call(ctx, args...);
}
private:
struct IWrap {
virtual void call(const EmitContext&, const Args&... args) = 0;
};
template <typename F> struct Wrap : IWrap {
Wrap(F func) : f_(std::move(func)) {}
void call(const EmitContext& emit_context, const Args&... args) override {
if constexpr (std::is_invocable_v<F, EmitContext const&, Args...>)
std::invoke(f_, emit_context, args...);
else
std::invoke(f_, args...);
}
F f_;
};
std::shared_ptr<IWrap> impl_;
};
#include <iomanip>
int main() {
Event<int, std::string_view> e;
auto simple = [](int i, std::string_view s)
{
std::cout << "Simple i:" << i << " s:" << std::quoted(s) << "\n";
};
auto with_ctx = [](EmitContext const& ctx, int i, std::string_view s) {
std::cout << "With context{" << ctx.some_value << "}: i:" << i
<< " s:" << std::quoted(s) << "\n";
};
e.RegisterCallbackFunction(simple);
e.Emit(42, "doodle");
e.RegisterCallbackFunction(with_ctx);
e.Emit(99, "labra");
}
Prints
g++ -std=c++17 -O2 -Wall -pedantic -pthread main.cpp && ./a.out
Simple i:42 s:"doodle"
With context{123}: i:99 s:"labra"
#include <iostream>
#include <type_traits>
class CL
{
public:
CL(){}
CL( int ) = delete;
};
template < class T >
class Creator
{
public:
template< std::enable_if_t< std::is_constructible<T, int>::value, int > = 0 >
static T* Create( int arg ) // 1
{
return new T( arg );
}
template< std::enable_if_t< std::is_default_constructible<T>::value && !std::is_constructible<T, int>::value, int > = 0 >
static T* Create( int arg ) // 2
{
return new T();
}
};
int main()
{
Creator<CL>::Create( 2 );
}
Here I give error that the first Create function can't deduce template argument, but then I commented it, the second overload is works fine. Why SFINAE doesn't work on first overload?
Your method is not template, it is your class which is template. You had to change
template<typename U = T,
std::enable_if_t< std::is_constructible<U, int>::value, int > = 0 >
static T* Create( int arg ) // 1
{
return new T( arg );
}
template<typename U = T,
std::enable_if_t< std::is_default_constructible<U>::value
&& !std::is_constructible<U, int>::value, int > = 0 >
static T* Create( int arg ) // 2
{
return new T();
}
When you defined a template in C++ (with type parameter) you can pass as type actually a pointer to a type, for example:
MyClass<Foo*>... // edited
I wonder if this is really used? Because such question is too broad, let's focus on core C++, meaning -- is it used in STL or Boost? If yes (in STL/Boost) for what purpose?
Please note that I am asking about passing pointer as an argument (from OUTSIDE). Using pointer to passed argument INSIDE template is another story, and I don't ask about that.
Update
The difference between passing a pointer and using a pointer.
Take a look at those posts:
How is vector implemented in C++ and
passing pointer type to template argument .
myname<char*>(...
myname is a function. The type which is passed to template (template, not a function) is pointer to char.
Now, using a pointer inside:
template <class T...
class vector {
private:
T* data_;
You pass an int (for example) but nothing stops the template of using a pointer to it.
I am interested in the first, not the second, case.
A pointer is just a type, so anywhere you can use some_template<T> you might also want to use some_template<T*>
The parts of the standard library based in the STL use std::iterator_traits<Iter> in many places, and Iter might be a pointer type.
Some implementations of std::unique_ptr<T, D> use a data member of type std::tuple<T*, D> (e.g. this is true for GCC's implementation, but this is an implementation detail that you should not care about).
Like I commented, I remembered a use case where functions would be registered as event callbacks by their actual address.
This way the trampoline functions to invoke member functions would be generated statically for each member function registered.
I'm not sure this is something I'd actually have a use for, but it does demonstrate a (contrived?) way in which pointer-to-function template arguments can be used.
#include <iostream>
#include <vector>
const static auto null = nullptr;
template<typename TFuncSignature>
class Callback;
template<typename R, typename A1>
class Callback<R (A1)> {
public:
typedef R (*TFunc)(void*, A1);
Callback() : obj(0), func(0) {}
Callback(void* o, TFunc f) : obj(o), func(f) {}
R operator()(A1 a1) const {
return (*func)(obj, a1);
}
typedef void* Callback::*SafeBoolType;
operator SafeBoolType () const {
return func != 0? &Callback::obj : 0;
}
bool operator! () const {
return func == 0;
}
bool operator== ( const Callback<R (A1)>& right ) const {
return obj == right.obj && func == right.func;
}
bool operator!= ( const Callback<R (A1)>& right ) const {
return obj != right.obj || func != right.func;
}
private:
void* obj;
TFunc func;
};
template<typename R, class T, typename A1>
struct DeduceMemCallbackTag {
template<R (T::*Func)(A1)>
static R Wrapper(void* o, A1 a1) {
return (static_cast<T*>(o)->*Func)(a1);
}
template<R (T::*Func)(A1)>
inline static Callback<R (A1)> Bind(T* o) {
return Callback<R (A1)>(o, &DeduceMemCallbackTag::Wrapper<Func>);
}
};
template<typename R, typename A1>
struct DeduceStaticCallbackTag {
template<R (*Func)(A1)>
static R Wrapper(void*, A1 a1) {
return (*Func)(a1);
}
template<R (*Func)(A1)>
inline static Callback<R (A1)> Bind( ) {
return Callback<R (A1)>( 0, &DeduceStaticCallbackTag::Wrapper<Func> );
}
};
template<typename R, class T, typename A1>
DeduceMemCallbackTag<R, T, A1> DeduceMemCallback(R (T::*)(A1)) {
return DeduceMemCallbackTag<R, T, A1>();
}
template<typename R, typename A1>
DeduceStaticCallbackTag<R, A1> DeduceStaticCallback(R (*)(A1)) {
return DeduceStaticCallbackTag<R, A1>();
}
template <typename T1> class Event {
public:
typedef void(* TSignature)( T1 );
typedef Callback<void( T1 )> TCallback;
protected:
std::vector<TCallback> invocations;
std::vector<Event<T1>*> events;
public:
const static int ExpectedFunctorCount = 2;
Event () : invocations(), events() {
invocations.reserve( ExpectedFunctorCount );
events.reserve( ExpectedFunctorCount );
}
template <void (* TFunc)(T1)> void Add ( ) {
TCallback c = DeduceStaticCallback( TFunc ).template Bind< TFunc >( );
invocations.push_back( c );
}
template <typename T, void (T::* TFunc)(T1)> void Add ( T& object ) {
Add<T, TFunc>( &object );
}
template <typename T, void (T::* TFunc)(T1)> void Add ( T* object ) {
TCallback c = DeduceMemCallback( TFunc ).template Bind< TFunc >( object );
invocations.push_back( c );
}
void Invoke ( T1 t1 ) {
size_t i;
for ( i = 0; i < invocations.size(); ++i ) {
invocations[i]( t1 );
}
for ( i = 0; i < events.size(); ++i ) {
(*events[i])( t1 );
}
}
void operator() ( T1 t1 ) {
Invoke( t1 );
}
size_t InvocationCount ( ) {
return events.size( ) + invocations.size( );
}
template <void (* TFunc)(T1)> bool Remove ( ) {
TCallback target = DeduceStaticCallback( TFunc ).template Bind< TFunc >( );
for ( size_t i = 0; i < invocations.size(); ++i ) {
TCallback& inv = invocations[i];
if ( target == inv ) {
invocations.erase( invocations.begin() + i );
return true;
}
}
return false;
}
template <typename T, void (T::* TFunc)(T1)> bool Remove ( T& object ) {
return Remove<T, TFunc>( &object );
}
template <typename T, void (T::* TFunc)(T1)> bool Remove ( T* object ) {
TCallback target = DeduceMemCallback( TFunc ).template Bind< TFunc >( object );
for ( size_t i = 0; i < invocations.size(); ++i ) {
TCallback& inv = invocations[i];
if ( target == inv ) {
invocations.erase( invocations.begin() + i );
return true;
}
}
return false;
}
};
namespace IntStatic {
void VoidTest () { std::cout << "INTO THE VOID" << std::endl; }
void IntTest (int num) { std::cout << "Got myself a " << num << " !" << std::endl; }
void IntTest2 (int num) { std::cout << "Now _I_ Got myself a " << num << " !" << std::endl; }
}
struct Int {
void Test (int num) { std::cout << num << " on the inside of a class... ?" << std::endl; }
void Test2 (int num) { std::cout << num << " on the inside of a struct, yo !" << std::endl; }
static void Test3(int snum) { std::cout << snum << " on the inside of a class... ?" << std::endl; }
};
int main(int argc, char* argv[]) {
Event<int> intev;
Int i;
intev.Add<Int, &Int::Test>(i);
intev.Add<&IntStatic::IntTest>();
intev.Add<&IntStatic::IntTest2>();
//intev.Add( Int::Test3 );
intev(20);
intev.Remove<&IntStatic::IntTest>();
intev.Remove<&IntStatic::IntTest>();
intev.Remove<Int, &Int::Test>(i);
//intev.Remove( Int::Test3 );
//intev.Remove( i, &Int::Test );
intev(20);
return 0;
}
The actual code is famously written by #ThePhD, credits to him. See it Live on Coliru
You could have different semantics by specialization, e.g.:
template <typename T> struct referred_sizeof {
static constexpr size_t value = sizeof(T);
};
Now, this could be specialized:
template <typename T> struct referred_sizeof<T*> {
static constexpr size_t value = sizeof(T);
};
template <typename T> struct referred_sizeof <boost::optional<T*> > {
static constexpr size_t value = sizeof(boost::optional<T*>) + sizeof(T);
};
Which makes the behaviour:
static_assert(referred_sizeof <int>::value == referred_sizeof <int*>::value, "passes");
This application is what others referred to in comments as implementing traits classes.
Full sample, adding specialization for boost::tuple<...> just for fun: See it Live On Coliru
int main()
{
report<double>();
report<double *>();
report<boost::optional<double> >();
report<boost::optional<double> *>();
report<boost::optional<double *> *>();
report<boost::tuple<boost::optional<double *> *, double> >();
}
Prints
void report() [with T = double]: referred_sizeof is 8
void report() [with T = double*]: referred_sizeof is 8
void report() [with T = boost::optional<double>]: referred_sizeof is 16
void report() [with T = boost::optional<double>*]: referred_sizeof is 16
void report() [with T = boost::optional<double*>*]: referred_sizeof is 24
void report() [with T = boost::tuples::tuple<boost::optional<double*>*, double>]: referred_sizeof is 40
Full implementation for reference
#include <iostream>
#include <boost/optional.hpp>
#include <boost/tuple/tuple.hpp>
template <typename... Ts> struct referred_sizeof;
// base cases
template <typename T> struct referred_sizeof<T> {
static constexpr size_t value = sizeof(T);
};
template <typename T> struct referred_sizeof<T*> {
static constexpr size_t value = referred_sizeof<T>::value;
};
template <typename T> struct referred_sizeof<boost::optional<T*> > {
static constexpr size_t value = sizeof(boost::optional<T*>) + referred_sizeof<T>::value;
};
template <typename... Ts> struct referred_sizeof<boost::tuple<Ts...> > {
static constexpr size_t value = referred_sizeof<Ts...>::value; // TODO take into account padding/alignment overhead?
};
static_assert(referred_sizeof<int>::value == referred_sizeof<int*>::value, "passes");
template <typename T1, typename... Ts> struct referred_sizeof<T1, Ts...> {
static constexpr size_t value = referred_sizeof<T1>::value + referred_sizeof<Ts...>::value;
};
template <typename T>
void report()
{
std::cout << __PRETTY_FUNCTION__ << ": referred_sizeof is " << referred_sizeof<T>::value << "\n";
}
Say I have 5 classes, A-E.
I want to create a class Gadget that can be constructed from 0-5 parameters which are constrained to be a const reference to types A,B,C,D, or E in any order and without duplicates.
What is the cleanest way to implement this?
The following solves your problem:
#include <type_traits>
#include <tuple>
// find the index of a type in a list of types,
// return sizeof...(Ts) if T is not found
template< typename T, typename... Ts >
struct index_by_type : std::integral_constant< std::size_t, 0 > {};
template< typename T, typename... Ts >
struct index_by_type< T, T, Ts... > : std::integral_constant< std::size_t, 0 >
{
static_assert( index_by_type< T, Ts... >::value == sizeof...( Ts ), "duplicate type detected" );
};
template< typename T, typename U, typename... Ts >
struct index_by_type< T, U, Ts... > : std::integral_constant< std::size_t, index_by_type< T, Ts... >::value + 1 > {};
// get the element from either "us" if possible...
template< std::size_t I, std::size_t J, typename T, typename... Us, typename... Ts >
auto get_by_index( const std::tuple< Us... >&, const std::tuple< Ts... >& ts )
-> typename std::enable_if< I == sizeof...( Us ), const T& >::type
{
return std::get< J >( ts );
}
// ...get the element from "ts" otherwise
template< std::size_t I, std::size_t J, typename T, typename... Us, typename... Ts >
auto get_by_index( const std::tuple< Us... >& us, const std::tuple< Ts... >& )
-> typename std::enable_if< I != sizeof...( Us ), const T& >::type
{
return std::get< I >( us );
}
// helper to validate that all Us are in Ts...
template< bool > struct invalide_type;
template<> struct invalide_type< true > : std::true_type {};
template< std::size_t... > void validate_types() {}
template< typename T >
struct dflt
{
static const T value;
};
template< typename T >
const T dflt< T >::value;
// reorder parameters
template< typename... Ts, typename... Us >
std::tuple< const Ts&... > ordered_tie( const Us&... us )
{
auto t1 = std::tie( us... );
auto t2 = std::tie( dflt< Ts >::value... );
validate_types< invalide_type< index_by_type< const Us&, const Ts&... >::value != sizeof...( Ts ) >::value... >();
return std::tie( get_by_index< index_by_type< const Ts&, const Us&... >::value,
index_by_type< const Ts&, const Ts&... >::value, Ts >( t1, t2 )... );
}
struct A {};
struct B {};
struct C {};
struct Gadget
{
A a;
B b;
C c;
explicit Gadget( const std::tuple< const A&, const B&, const C& >& t )
: a( std::get<0>(t) ),
b( std::get<1>(t) ),
c( std::get<2>(t) )
{}
template< typename... Ts >
Gadget( const Ts&... ts ) : Gadget( ordered_tie< A, B, C >( ts... ) ) {}
};
int main()
{
A a;
B b;
C c;
Gadget g1( a, b, c );
Gadget g2( b, c, a );
Gadget g3( a, b ); // uses a default-constructed C
Gadget g4( a, c ); // uses a default-constructed B
Gadget g5( c ); // uses a default-constructed A and B
Gadget g6; // uses a default-constructed A, B and C
// fails to compile:
// Gadget gf1( a, a ); // duplicate type
// Gadget gf2( a, b, 42 ); // invalid type
}
Live example
Easy just use variadic templates and a static_assert
template <typename ... Types>
struct thing
{
static_assert(sizeof...(Types) <= 5,"Too many objects passed");
};
int main()
{
thing<int,float,double,int,int> a;
return 0;
}
Preventing duplicates might be tricky, I still have to think of that one.
Honestly I can't think of any un-painful way to ensure that all the types are different but the solution will likely involve std::is_same one definite way to make it work would be to have specializations for 0 - 5 types and use a static_assert to check all the combinations in each specialization, this will definitely be a pain though.
EDIT: well this was fun
template <typename ... Types>
struct thing
{
static_assert(sizeof ... (Types) <= 5,"Too big");
};
template <>
struct thing<> {};
template <typename A>
struct thing<A>{};
template <typename A, typename B>
struct thing<A,B>
{
static_assert(!std::is_same<A,B>::value,"Bad");
};
template <typename A, typename B, typename C>
struct thing<A,B,C>
{
static_assert(!std::is_same<A,B>::value &&
!std::is_same<A,C>::value &&
!std::is_same<C,B>::value,"Bad");
};
template <typename A, typename B, typename C, typename D>
struct thing<A,B,C,D>
{
static_assert(!std::is_same<A,B>::value &&
!std::is_same<A,C>::value &&
!std::is_same<C,B>::value &&
!std::is_same<C,D>::value &&
!std::is_same<B,D>::value &&
!std::is_same<A,D>::value,"Bad");
};
template <typename A, typename B, typename C, typename D, typename E>
struct thing<A,B,C,D,E>
{
static_assert(!std::is_same<A,B>::value &&
!std::is_same<A,C>::value &&
!std::is_same<C,B>::value &&
!std::is_same<C,D>::value &&
!std::is_same<B,D>::value &&
!std::is_same<A,D>::value &&
!std::is_same<A,E>::value &&
!std::is_same<B,E>::value &&
!std::is_same<C,E>::value &&
!std::is_same<D,E>::value,"Bad");
};
int main()
{
thing<> a;
thing<int,float,int> b; //error
thing<int,float,double,size_t,char> c;
thing<int,float,double,size_t,char,long> d; //error
return 0;
}
To create a more general approach what you have to do is create a compile time combination meta function
The question asks for a Gaget class that can be constructed with [0-5] number of parameters constrained to 5 different types without duplication, and with any order. With the help of templates it's doable; below is an example for two parameters, and it's easily extensible to 5 parameters.
class A
{
};
class B
{
};
template<typename T> struct is_A
{
enum { value = false };
};
template<> struct is_A<A>
{
enum { value = true };
};
template<typename T> struct is_B
{
enum { value = false };
};
template<> struct is_B<B>
{
enum { value = true };
};
template <bool V> struct bool_to_count
{
enum {value = V ? 1 : 0};
};
class Gaget
{
public:
template <typename T1> Gaget(const T1& t1)
{
static_assert(is_A<T1>::value || is_B<T1>::value, "T1 can only be A or B");
if (is_A<T1>::value)
{
m_a = *reinterpret_cast<const A*>(&t1);
}
if (is_B<T1>::value)
{
m_b = *reinterpret_cast<const B*>(&t1);
}
}
template <typename T1, typename T2> Gaget(const T1& t1, const T2& t2)
{
static_assert(is_A<T1>::value || is_B<T1>::value, "T1 can only be A or B");
static_assert(is_A<T2>::value || is_B<T2>::value, "T2 can only be A or B");
const int countA = bool_to_count<is_A<T1>::value>::value
+ bool_to_count<is_A<T2>::value>::value;
static_assert(countA == 1, "One and only one A is allowed");
const int countB = bool_to_count<is_B<T1>::value>::value
+ bool_to_count<is_B<T2>::value>::value;
static_assert(countA == 1, "One and only one B is allowed");
if(is_A<T1>::value)
{
// it's safe because it's only executed when T1 is A;
// same with all following
m_a = *reinterpret_cast<const A*>(&t1);
}
if(is_B<T1>::value)
{
m_b = *reinterpret_cast<const B*>(&t1);
}
if (is_A<T2>::value)
{
m_a = *reinterpret_cast<const A*>(&t2);
}
if (is_B<T2>::value)
{
m_b = *reinterpret_cast<const B*>(&t2);
}
}
private:
A m_a;
B m_b;
};
void foo(const A& a, const B& b)
{
auto x1 = Gaget(b,a);
auto x2 = Gaget(a,b);
auto x3 = Gaget(a);
auto x4 = Gaget(b);
// auto x5 = Gaget(a,a); // error
// auto x6 = Gaget(b,b); // error
}
If you are willing to make compromises on the syntax, you can do it with the Builder pattern. The usage would look like this:
Gadget g = Gadget::builder(c)(a)(b)();
Yes, this syntax is not very nice and perhaps a bit obscure but it is a reasonable compromise. The good news is that you avoid the combinatorial explosion: This solutions scales linearly with the number of arguments. One downside is that duplicate arguments are detected only at runtime.
Sample code for 3 types (may contain errors):
#include <iostream>
#include <stdexcept>
struct A { char value = ' '; };
struct B { char value = ' '; };
struct C { char value = ' '; };
struct state { A a; B b; C c; };
class Gadget {
private:
Gadget(state s) : s(s) { };
state s;
public:
class builder {
public:
template <class T>
builder(T t) { reference(t) = t; }
template <class T>
builder& operator()(T t) { return assign(reference(t), t); }
Gadget operator()() { return Gadget(s); }
private:
template <class T>
builder& assign(T& self, T t) {
if (self.value != ' ')
throw std::logic_error("members can be initialized only once");
self = t;
return *this;
}
A& reference(A ) { return s.a; }
B& reference(B ) { return s.b; }
C& reference(C ) { return s.c; }
state s;
};
friend std::ostream& operator<<(std::ostream& out, const Gadget& g) {
out << "A: " << g.s.a.value << std::endl;
out << "B: " << g.s.b.value << std::endl;
return out << "C: " << g.s.c.value << std::endl;
}
};
int main() {
A a; a.value = 'a';
B b; b.value = 'b';
C c; c.value = 'c';
Gadget g = Gadget::builder(c)(a)(b)();
std::cout << "Gadget:\n" << g << std::endl;
}
Far from perfect but I personally find it easier to read and understand than the solutions using template metaprogramming.
Here is a working solution, still not sure of the optimal solution.
#include <iostream>
#include <boost/mpl/vector.hpp>
#include <boost/mpl/set.hpp>
#include <boost/mpl/size.hpp>
#include <boost/mpl/placeholders.hpp>
#include <boost/mpl/insert.hpp>
#include <boost/mpl/int.hpp>
#include <boost/mpl/if.hpp>
#include <boost/mpl/has_key.hpp>
#include <boost/type_traits/is_same.hpp>
#include <boost/fusion/include/vector.hpp>
#include <boost/fusion/include/for_each.hpp>
struct A{
A() { std::cout << "A default constructor" << std::endl; }
~A() { std::cout << "A destructor" << std::endl; }
A( const A& ) { std::cout << "A copy constructor" << std::endl; }
A( A&& ) { std::cout << "A move constructor" << std::endl; }
};
struct B{
B() { std::cout << "B default constructor" << std::endl; }
~B() { std::cout << "B destructor" << std::endl; }
B( const B& ) { std::cout << "B copy constructor" << std::endl; }
B( B&& ) { std::cout << "B move constructor" << std::endl; }
};
struct C{
C() { std::cout << "C default constructor" << std::endl; }
~C() { std::cout << "C destructor" << std::endl; }
C( const C& ) { std::cout << "C copy constructor" << std::endl; }
C( C&& ) { std::cout << "C move constructor" << std::endl; }
};
struct D{
D() { std::cout << "D default constructor" << std::endl; }
~D() { std::cout << "D destructor" << std::endl; }
D( const D& ) { std::cout << "D copy constructor" << std::endl; }
D( D&& ) { std::cout << "D move constructor" << std::endl; }
};
struct E{
E() { std::cout << "E default constructor" << std::endl; }
~E() { std::cout << "E destructor" << std::endl; }
E( const E& ) { std::cout << "E copy constructor" << std::endl; }
E( E&& ) { std::cout << "E move constructor" << std::endl; }
};
class Gadget
{
struct call_setters
{
Gadget& self;
call_setters( Gadget& self_ ) : self( self_ ){}
template< typename T >
void operator()( T& t ) const
{
self.set( t );
}
};
public:
template< typename... Args >
Gadget( const Args&... args )
{
using namespace boost::mpl;
using namespace boost::mpl::placeholders;
typedef vector<A, B, C, D, E> allowed_args;
static_assert(sizeof...(Args) <= size<allowed_args>::value, "Too many arguments");
typedef typename fold< vector<Args...>
, set0<>
, insert<_1, _2>
>::type unique_args;
static_assert(size<unique_args>::value == sizeof...(Args), "Duplicate argument types");
typedef typename fold< allowed_args
, int_<0>
, if_< has_key<unique_args, _2 >, next<_1>, _1 >
>::type allowed_arg_count;
static_assert(allowed_arg_count::value == sizeof...(Args), "One or more argument types are not allowed");
namespace bf = boost::fusion;
bf::for_each( bf::vector<const Args&...>( args... ), call_setters{ *this } );
}
void set( const A& ) { std::cout << "Set A" << std::endl; }
void set( const B& ) { std::cout << "Set B" << std::endl; }
void set( const C& ) { std::cout << "Set C" << std::endl; }
void set( const D& ) { std::cout << "Set D" << std::endl; }
void set( const E& ) { std::cout << "Set E" << std::endl; }
};
int main()
{
Gadget{ A{}, E{}, C{}, D{}, B{} };
}
Live Demo
I have a working functor_wrapper class for 2 parameters that I'm having trouble generalising into variadic template parameters.
In particular, I'm having trouble getting the syntax right while converting this line in 2 parameters to variadics:
2 Parameter CODE
return m_cb( dynamic_cast<T const&>( t ), dynamic_cast<U const&>( u ));
Broken Variadic CODE
return m_cb( dynamic_cast<I const&>( t ),
dynamic_cast<typename... const& Cs>( u... ));
For anyone who wants to know, this wrapper class is part of a multiple_dispatch class. In order to provide a common type/interface to the client, I have this Functor_Wrapper class and operator() that takes the parent class type. When dispatching, I downcast by dynamic_cast<>ing the parameters down to the correct types.
UPDATE
I just realised that I have another problem: my virtual operator() in my Parent_Functor_Wrapper needs to be converted to n parameters of P const& so I need to unroll the signature somehow!
The same applies to the concrete implementation of operator() in Functor_Wrapper.
UPDATE 2
Posting complete code so the programming context can be better understood.
UPDATE 3
Accepting ildjam's answer because it addresses the original problem. I need to think some more about how to deal with the pure virtual operator(). Meanwhile, the code below works fine if you comment out #define USE_VARIADICS, and run the hard-coded version. I will post a separate question if I cannot resolve this issue.
CODE
#include <typeinfo>
#include <functional>
#include <stdexcept>
#include <cassert>
#include <map>
#include <array>
#include <iostream>
#define USE_VARIADICS
#ifdef USE_VARIADICS
template<typename P,typename R,typename... Cs>
struct Parent_Functor_Wrapper
{
using cb_func = std::function<R(P const&, P const& )>;
virtual R operator()( P const&, P const& ) = 0;
};
// parent class, return type, concrete classes...
template<typename P,typename R,typename I,typename... Cs>
struct Functor_Wrapper :
public Parent_Functor_Wrapper<P,R,Cs...>
{
using cb_func = std::function<R(I const&, Cs const& ...)>;
cb_func m_cb;
// dynamic_cast<typename... const& Cs>( u... ) -> dynamic_cast<Cs const&>(u)...
Functor_Wrapper( cb_func cb ) : m_cb( cb ) { }
virtual R operator()( P const& t, Cs const& ... u ) override
{
return m_cb( dynamic_cast<I const&>( t ),
dynamic_cast<Cs const&>( u )...);
}
};
#else
template<typename P,typename R>
struct Parent_Functor_Wrapper
{
using cb_func = std::function<R(P const&, P const& )>;
virtual R operator()( P const&, P const& ) = 0;
};
template<typename P,typename R,typename T,typename U>
struct Functor_Wrapper :
public Parent_Functor_Wrapper<P,R>
{
using cb_func = std::function<R(T const&, U const&)>;
cb_func m_cb;
Functor_Wrapper( cb_func cb ) : m_cb( cb ) { }
virtual R operator()( P const& t, P const& u ) override
{
return m_cb( dynamic_cast<T const&>( t ), dynamic_cast<U const&>( u ));
}
};
#endif
template<typename T,unsigned N>
struct n_dimension
{
using type = typename n_dimension<T,N-1>::type;
};
template<typename T>
struct n_dimension<T,1>
{
using type = std::tuple<T>;
};
template<typename K>
K gen_key( K* p=nullptr, size_t idx=0 )
{
return *p;
};
template<typename K,typename T,typename... Ts>
K gen_key( K* p=nullptr, size_t idx=0 )
{
K m_instance;
if ( p==nullptr )
p = &m_instance;
*(p->begin() + idx) = typeid( T ).hash_code();
gen_key<K,Ts...>( p, ++idx );
return m_instance;
};
template<typename F,typename P,typename... Cs>
struct multiple_dispatcher { multiple_dispatcher() { } };
template<typename F,typename P,typename I,typename... Cs>
struct multiple_dispatcher<F,P,I,Cs...>
{
using functor_type = Parent_Functor_Wrapper<P,std::string>;
using key_type = std::array<size_t,F::n_dimension::val>;
using map_type = std::map<key_type,functor_type*>;
using value_type = typename map_type::value_type;
map_type m_jump_table;
multiple_dispatcher( std::initializer_list<value_type> const& cb_mapping )
{
for( const auto& p : cb_mapping )
m_jump_table.insert( p );
}
template< typename T, typename U >
typename F::return_type operator()( T& x, U& y )
{
auto k = key_type{ { x.get_id(), y.get_id() } };
auto func = m_jump_table.at( k );
return (*func)( x, y );
}
size_t size() const { return m_jump_table.size(); }
};
// =============================================================================
struct A { virtual ~A() { }
virtual size_t get_id() const { return typeid( A ).hash_code(); }
};
struct B : public A { virtual ~B() { }
virtual size_t get_id() const override { return typeid( B ).hash_code(); }
};
struct C : public A { int x; virtual ~C() { }
virtual size_t get_id() const override { return typeid( C ).hash_code(); }
};
struct Functor_Pairs
{
using return_type = std::string;
enum n_dimension { val = 2 };
static std::string MyFn(A const& arg1, A const& arg2) { return "A,A"; }
static std::string MyFn(A const& arg1, B const& arg2) { return "A,B"; }
static std::string MyFn(A const& arg1, C const& arg2) { return "A,C"; }
static std::string MyFn(B const& arg1, A const& arg2) { return "B,A"; }
static std::string MyFn(B const& arg1, B const& arg2) { return "B,B"; }
static std::string MyFn(B const& arg1, C const& arg2) { return "B,C"; }
static std::string MyFn(C const& arg1, A const& arg2) { return "C,A"; }
static std::string MyFn(C const& arg1, B const& arg2) { return "C,B"; }
static std::string MyFn(C const& arg1, C const& arg2) { return "C,C"; }
};
// =============================================================================
std::string (*MyFun_aa)(A const&, A const&) = &Functor_Pairs::MyFn;
std::string (*MyFun_ab)(A const&, B const&) = &Functor_Pairs::MyFn;
std::string (*MyFun_ac)(A const&, C const&) = &Functor_Pairs::MyFn;
std::string (*MyFun_ba)(B const&, A const&) = &Functor_Pairs::MyFn;
std::string (*MyFun_bb)(B const&, B const&) = &Functor_Pairs::MyFn;
std::string (*MyFun_bc)(B const&, C const&) = &Functor_Pairs::MyFn;
std::string (*MyFun_ca)(C const&, A const&) = &Functor_Pairs::MyFn;
std::string (*MyFun_cb)(C const&, B const&) = &Functor_Pairs::MyFn;
std::string (*MyFun_cc)(C const&, C const&) = &Functor_Pairs::MyFn;
Functor_Wrapper<A,std::string,A,A> w_aa( MyFun_aa );
Functor_Wrapper<A,std::string,A,B> w_ab( MyFun_ab );
Functor_Wrapper<A,std::string,A,C> w_ac( MyFun_ac );
Functor_Wrapper<A,std::string,B,A> w_ba( MyFun_ba );
Functor_Wrapper<A,std::string,B,B> w_bb( MyFun_bb );
Functor_Wrapper<A,std::string,B,C> w_bc( MyFun_bc );
Functor_Wrapper<A,std::string,C,A> w_ca( MyFun_ca );
Functor_Wrapper<A,std::string,C,B> w_cb( MyFun_cb );
Functor_Wrapper<A,std::string,C,C> w_cc( MyFun_cc );
// =============================================================================
int main( int argc, char* argv[] )
{
B b;
C c;
A& arg1 = b;
A& arg2 = c;
using md_type = multiple_dispatcher<Functor_Pairs,A,B,C>;
using K = md_type::key_type;
md_type md
{
std::make_pair( gen_key<K,A,A>(), &w_aa ),
std::make_pair( gen_key<K,A,B>(), &w_ab ),
std::make_pair( gen_key<K,A,C>(), &w_ac ),
std::make_pair( gen_key<K,B,A>(), &w_ba ),
std::make_pair( gen_key<K,B,B>(), &w_bb ),
std::make_pair( gen_key<K,B,C>(), &w_bc ),
std::make_pair( gen_key<K,C,A>(), &w_ca ),
std::make_pair( gen_key<K,C,B>(), &w_cb ),
std::make_pair( gen_key<K,C,C>(), &w_cc )
};
std::cerr << "N = " << md.size() << std::endl;
std::cerr << "RESULT = " << md( arg1, arg2 ) << std::endl;
assert( !std::string( "B,C" ).compare( md( arg1, arg2 )) );
#if 0
std::cerr << typeid( A ).hash_code() << std::endl;
std::cerr << typeid( B ).hash_code() << std::endl;
std::cerr << typeid( C ).hash_code() << std::endl;
#endif
return 0;
}
You need to change:
dynamic_cast<typename... const& Cs>( u... )
to:
dynamic_cast<Cs const&>( u )...
The former (invalid syntax aside) is attempting to expand Cs into a single dynamic_cast while the latter expands each type in Cs into its own dynamic_cast.
That being said, I don't think your override will work since your base's operator() signature is R operator()( P const&, P const& ) and your derived's signature is not (unless Cs const&... coincidentally happens to expand into P const&)…