Related
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... );
}
};
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";
}
I would like to use templates for optimization as described here. However, with a growing number of bool template arguments, instantiating the template might have too many branches. And it gets even more branchy if you use larger enums instead of bools.
#include <iostream>
using namespace std;
template <bool b1, bool b2>
int HeavyLoop_impl(int arg)
{
for (int i = 0; i < 10000000; i++)
{
// b1 is known at compile-time, so this branch will be eliminated
if (b1) { arg += 1; }
else { arg += 2; }
// b2 is known at compile-time, so this branch will be eliminated
if (b2) { arg += 10; }
else { arg += 20; }
}
return arg;
}
// This function could be generated automatically
void HeavyLoop(bool b1, bool b2, int arg)
{
int res;
if (b1) {
if (b2) { res = HeavyLoop_impl<true, true>(arg); }
else { res = HeavyLoop_impl<true, false>(arg); }
} else {
if (b2) { res = HeavyLoop_impl<false, true>(arg); }
else { res = HeavyLoop_impl<false, false>(arg); }
}
cout << "res: "<<res<<endl;
}
int main(int argc, char**argv)
{
bool b1 = true;
bool b2 = false;
int arg = 0;
HeavyLoop(b1, b2, arg);
return 0;
}
Is there any way to automatically generate the HeavyLoop function? I would like something like this:
vars_to_template_function<bool, bool>(HeavyLoop_impl, b1, b2, arg);
Would that be possible somehow? Thanks for any hints.
Note: this is only a very simplified example. The actual loop is is of course more complicated :o)
I decided to have some more fun with the code, here's an improved version over my first attempt which has the following benefits:
Supports enum types
Explicitly specify how many parameters should be converted
Generic implementation for the complicated part, one small helper for each function that uses it.
The code:
#include <iostream>
#include <utility>
#include <type_traits>
// an enum we would like to support
enum class tribool { FALSE, TRUE, FILE_NOT_FOUND };
// declare basic generic template
// (independent of a specific function you'd like to call)
template< template< class > class CB, std::size_t N, typename = std::tuple<> >
struct var_to_template;
// register types that should be supported
template< template< class > class CB, std::size_t N, typename... Cs >
struct var_to_template< CB, N, std::tuple< Cs... > >
{
// bool is pretty simple, there are only two values
template< typename R, typename... Args >
static R impl( bool b, Args&&... args )
{
return b
? var_to_template< CB, N-1, std::tuple< Cs..., std::true_type > >::template impl< R >( std::forward< Args >( args )... )
: var_to_template< CB, N-1, std::tuple< Cs..., std::false_type > >::template impl< R >( std::forward< Args >( args )... );
}
// for each enum, you need to register all its values
template< typename R, typename... Args >
static R impl( tribool tb, Args&&... args )
{
switch( tb ) {
case tribool::FALSE:
return var_to_template< CB, N-1, std::tuple< Cs..., std::integral_constant< tribool, tribool::FALSE > > >::template impl< R >( std::forward< Args >( args )... );
case tribool::TRUE:
return var_to_template< CB, N-1, std::tuple< Cs..., std::integral_constant< tribool, tribool::TRUE > > >::template impl< R >( std::forward< Args >( args )... );
case tribool::FILE_NOT_FOUND:
return var_to_template< CB, N-1, std::tuple< Cs..., std::integral_constant< tribool, tribool::FILE_NOT_FOUND > > >::template impl< R >( std::forward< Args >( args )... );
}
throw "unreachable";
}
// in theory you could also add int, long, ... but
// you'd have to switch on every possible value that you want to support!
};
// terminate the recursion
template< template< class > class CB, typename... Cs >
struct var_to_template< CB, 0, std::tuple< Cs... > >
{
template< typename R, typename... Args >
static R impl( Args&&... args )
{
return CB< std::tuple< Cs... > >::template impl< R >( std::forward< Args >( args )... );
}
};
// here's your function with the template parameters
template< bool B, tribool TB >
int HeavyLoop_impl( int arg )
{
for( int i = 0; i < 10000000; i++ ) {
arg += B ? 1 : 2;
arg += ( TB == tribool::TRUE ) ? 10 : ( TB == tribool::FALSE ) ? 20 : 30;
}
return arg;
}
// a helper class, required once per function that you'd like to forward
template< typename > struct HeavyLoop_callback;
template< typename... Cs >
struct HeavyLoop_callback< std::tuple< Cs... > >
{
template< typename R, typename... Args >
static R impl( Args&&... args )
{
return HeavyLoop_impl< Cs::value... >( std::forward< Args >( args )... );
}
};
// and here, everything comes together:
int HeavyLoop( bool b, tribool tb, int arg )
{
// you provide the helper and the number of arguments
// that should be converted to var_to_template<>
// and you provide the return type to impl<>
return var_to_template< HeavyLoop_callback, 2 >::impl< int >( b, tb, arg );
}
int main()
{
bool b = true;
tribool tb = tribool::FALSE;
int arg = 0;
int res = HeavyLoop( b, tb, arg );
std::cout << "res: " << res << std::endl;
return 0;
}
And here's a live example in case you want to play with it.
Here's how you can do it:
#include <iostream>
using namespace std;
template <bool b1, bool b2>
struct HeavyLoopImpl
{
static int func(int arg)
{
for (int i = 0; i < 10000000; i++) {
arg += b1 ? 1 : 2;
arg += b2 ? 10 : 20;
}
return arg;
}
};
template <template<bool...> class Impl,bool...Bs>
struct GenericJump
{
template<typename... Args>
static int impl(Args&&... args)
{
return Impl<Bs...>::func(std::forward<Args>(args)...);
}
template<typename... Args>
static int impl(bool b, Args&&... args)
{
return b
? GenericJump<Impl,Bs...,true >::impl(std::forward<Args>(args)...)
: GenericJump<Impl,Bs...,false>::impl(std::forward<Args>(args)...);
}
};
int HeavyLoop(bool b1, bool b2, int arg)
{
return GenericJump<HeavyLoopImpl>::impl(b1,b2,arg);
}
int main()
{
bool b1 = true;
bool b2 = false;
int arg = 0;
int res = HeavyLoop(b1, b2, arg);
cout << "res: "<<res<<endl;
return 0;
}
This is basically Daniels solution, but it allows you to use functions other than HeavyLoop_impl() as implementation. Only being able to call a single template function kind of defeats the purpose of being a generic solution. The GenericJump template class can call other functions also. You only have to change the HeavyLoop_impl() template function into a template class with a static function func(). It works marvellously. It compiles with gcc 4.7.3 and gives the correct output.
The ideal generic solution really depends on what you want to vary. The possibilities for variation are:
The number of branches, and the types of the variables being branched on.
The operation to be performed and the number and types of its arguments.
I would recommend not making an completely generic solution unless you really need to vary all of those things. Consider only the things you want to vary and your life will be much easier.
Assuming that the total number of branches is less than 2^64, you can use a switch statement to do the dispatch. The following solution demonstrates how this could work:
template<unsigned permutation>
struct Permutation
{
static_assert(permutation < 4, "permutation must be in the range [0, 4)");
static const bool b1 = permutation & (1 << 0);
static const bool b2 = permutation & (1 << 1);
};
unsigned makePermutation(bool b1, bool b2)
{
return (b1 << 0) | (b2 << 1);
}
template<unsigned p>
int HeavyLoop_impl(int arg)
{
return HeavyLoop_impl<Permutation<p>::b1, Permutation<p>::b2>(arg);
}
int HeavyLoop_impl(unsigned permutation, int arg)
{
switch(permutation)
{
case 0: return HeavyLoop_impl<0>(arg);
case 1: return HeavyLoop_impl<1>(arg);
case 2: return HeavyLoop_impl<2>(arg);
case 3: return HeavyLoop_impl<3>(arg);
}
}
[Note: It would be trivial to use Boost.Preprocessor to generate the above switch statement.]
void HeavyLoop(bool b1, bool b2, int arg)
{
int res = HeavyLoop_impl(makePermutation(b1, b2), arg);
cout << "res: "<<res<<endl;
}
I think the best answer to your question is actually not to generate it automatically and leave it how you already have it in the question.
Making an automatic template function to generate the middle ground just obfuscates the invariant switching you're doing in the first place.
I much prefer to try to understand how the middle layer works in your question, than any of the answers people have offered for you.
I have a similar example. In my case I can apply a number of different operations between an array of values. The arrays are equal size. However I also have a structure that maps sub-ranges of the array with weight values that affect my operations.
So for instance, I might be working with arrays of 100 values, and have ranges with weights like this:
[0,25] rangeWeight = 0
[26,35] rangeWeight = 0.25
[36,50] rangeWeight = 0.5
[51,99] rangeWeight = 1.0
So each operation looks something like (pseudo):
for each subrange:
alias to the dst buffer
alias to the src buffer
determine the number of elements in the range
if there's any
weight = weightPassedIn * rangeWeight;
Op(dst, src, weight, numElements);
For me, there were several optimizations involving whether or not the destination is touched or not (if it was still at the cleared value, some assumptions can be made to simplify the math per operation), Also if the weight happens to be full, 1.0, there are other shortcuts.
At first I was writing the same loop over and over with all of the same setup, and once I refactored all the loops around each op into functions, I pretty much naturally had the form of your invariant wrapper. There actually turned out to be several wrappers which mainly just wrap the high level operation happening inside the loop, and otherwise just handle the individual optimizations like this:
if (weight == 1.0f)
{
if ( arrayIsCleared )
Blend<BlendOpSet, true, false>(otherBuff, subRangesMask, 1.0f);
else
Blend<BlendOpAccumulate, true, false>(otherBuff, subRangesMask, 1.0f);
}
else
{
if ( arrayIsCleared )
Blend<BlendOpSet, false, false>(otherBuff, subRangesMask, weight);
else
Blend<BlendOpAccumulate, false, false>(otherBuff, subRangesMask, weight);
}
Have you considered passing a function as template argument? This way you can tailor your inner loop to your own wishes.
Function passed as template argument
However, the function call will have a small overhead.
Here is another solution with boost::hana, which can handle also enums:
#include <cstdio>
#include <type_traits>
#include <boost/hana.hpp>
namespace hana = boost::hana;
template <typename F, typename TArgs, typename TIn, typename TOut>
void fun_arg_combinations_impl(F&& f, TArgs targs, TIn tin, TOut tout) {
if constexpr (hana::is_empty(tin)) {
hana::unpack(tout, f);
} else {
hana::for_each(hana::front(tin), [&](auto v){
if (v == hana::front(targs)) {
fun_arg_combinations_impl(f, hana::drop_front(targs), hana::drop_front(tin), hana::append(tout, v));
}
});
}
}
template <typename F, typename TArgs, typename TIn>
void fun_arg_combinations(F&& f, TArgs targs, TIn tin) {
fun_arg_combinations_impl(f, targs, tin, hana::tuple<>());
}
enum Shape {LINE, CIRCLE, SQUARE};
int main()
{
auto f_heavy_loop = [](auto b1t, auto b2t, auto st) {
constexpr bool b1 = decltype(b1t)::value;
constexpr bool b2 = decltype(b2t)::value;
constexpr Shape s = decltype(st )::value;
printf("out:%d %d %d\n", b1, b2, s);
};
//constexpr auto bools = hana::make_tuple(std::true_type{}, std::false_type{});
constexpr auto bools = hana::tuple<std::true_type, std::false_type>{};
constexpr auto shapes = hana::tuple<
std::integral_constant<Shape, LINE>,
std::integral_constant<Shape, CIRCLE>,
std::integral_constant<Shape, SQUARE>>{};
// Using volatile to not allow the compiler to optimize for hard-coded values
volatile bool b1 = true;
volatile bool b2 = false;
volatile Shape s = SQUARE;
fun_arg_combinations(
f_heavy_loop,
hana::make_tuple(b1 , b2 , s ),
hana::make_tuple(bools, bools, shapes));
}
b1, b2 and s inside the f_heavy_loop() lambda are all constexpr, so we can use if constexpr on them.
output:
out:1 0 2
Have a look at the generated assembly here: https://godbolt.org/z/nsF2l5
can someone explain why the compiler accepts only this code
template<typename L, size_t offset, typename enable_if< (offset<sizeof(L)), int >::type =0>
void a_function(){}
template<typename L, size_t offset, typename enable_if< (offset==sizeof(L)), int >::type =0>
void a_function(){}
but not this:
template<typename L, size_t offset, typename enable_if< (offset<sizeof(L)), int >::type =0>
class a_class{};
template<typename L, size_t offset, typename enable_if< (offset==sizeof(L)), int >::type =0>
class a_class{};
The compiler sees the second class template as a redefinition of the first.
You have to use specialization for classes. Typically, it is done with an extra parameter:
template <class P, class dummy = void>
class T;
template <class P>
class T<P, typename enable_if<something, void>::type> {
the real thing
};
Two class (or class template) declarations with the same name should always declare the same class or class template (or be a specialization, in which case it is still the same template).
You probably want to do something like this (ideone.com link):
#include <iostream>
template< typename PL, size_t pOffset, int pC = int( sizeof(PL) ) - int( pOffset ) >= 0 ? ( int( sizeof(PL) ) - int( pOffset ) == 0 ? 0 : 1 ) : -1 >
class TClass
{
};
template< typename PL, size_t pOffset >
class TClass< PL, pOffset, -1 >
{
public:
static int const sI = -1;
};
template< typename PL, size_t pOffset >
class TClass< PL, pOffset, 0 >
{
public:
static int const sI = 0;
};
template< typename PL, size_t pOffset >
class TClass< PL, pOffset, 1 >
{
public:
static int const sI = 1;
};
int main(void )
{
TClass< char, 0 > lC0;
TClass< char, 1 > lC1;
TClass< char, 2 > lC2;
std::cout << lC0.sI << " : " << lC1.sI << " : " << lC2.sI << std::endl;
return ( 0 );
}
Program output:
1 : 0 : -1
compiling with GCC i get always false from the following code. I believe this is a compiler bug, but someone may know better.
#include <iostream>
template< class T >
class has_apply {
typedef char yes[1];
typedef char no[2];
template< class U, U u >
struct binder {};
template< class U, unsigned n >
static yes& test( U*,
binder< void (U::*) ( const double& ),
&U::template apply< n >
>* = 0
);
template< class U, unsigned n >
static no& test( ... );
public:
static const bool result =
( sizeof( yes ) == sizeof( test< T, 0u >( (T*)(0) ) ) );
};
class A {
public:
template< unsigned n >
void apply( const double& );
};
int main()
{
std::cout << std::boolalpha << has_apply< A >::result << '\n';
return( 0 );
}
I can't claim to understand why, but I was able to make your code work by not taking U* and by pulling the declaration of the binder type out:
template< class T >
class has_apply {
public:
typedef char yes[1];
typedef char no[2];
template< class U, U u >
struct binder {};
typedef binder< void (T::*)(const double&), &T::template apply<0u> > b;
template < typename V, unsigned n >
struct declare
{
typedef binder< void (V::*)(const double&), &V::template apply<n> > type;
};
template< typename U, unsigned n >
static yes& test( typename declare<U,n>::type * );
template< class U, unsigned n >
static no& test( ... );
static const bool result =
( sizeof( yes ) == sizeof( test< T, 0u >( 0 ) ) );
};
You can actually simplify this a bit by removing the unsigned parameter from the function and just sticking 0u in the typedef within 'declare'.
Again, I can't explain why this intermediate metafunction is necessary but it was required and the above works in MSVC++ 2010
Andy Venikov's answer over in [comp.lang.c++.moderated] (I'm only taking credit for great google-foo (he he, I cheated)):
http://groups.google.com/group/comp.lang.c++.moderated/msg/93017cf706e08c9e
Like Noah I don't know why. Unlike Noah I didn't find a workable solution, but investigating the thing I managed to crash the MingW g++ 4.4.1 compiler (that is, an Internal Compiler Error). This was simply by inconsistently referring to apply as template and non-template:
#include <iostream>
template< class T >
class has_apply {
template< class U, U u >
struct binder {};
template< class U >
static double test(
U*,
binder<
void (U::*) ( const double& ),
//&U::template apply< 0 >
&U::apply
>* = 0
);
public:
static binder<
void (T::*) ( const double& ),
&T::template apply< 0 >
>* dummy();
static const bool result = sizeof( test( (T*)(0), dummy() ) );
};
class A {
public:
// template< unsigned n >
void apply( const double& );
};
int main()
{
std::cout << std::boolalpha << has_apply< A >::result << '\n';
return( 0 );
}
Effect on g++:
C:\test> g++ -std=c++98 y.cpp
y.cpp: In instantiation of 'has_apply':
y.cpp:38: instantiated from here
y.cpp:24: internal compiler error: in instantiate_type, at cp/class.c:6303
Please submit a full bug report,
with preprocessed source if appropriate.
See for instructions.
C:\test> _
He he...
PS: I'd love to post this as a "comment", since it's not an "answer".
This is not an answer to why it doesn't work. However, researching through the web, I've found some examples and eventually got to the following code, which may be even more to the point then what I've been trying.
I was trying to detect an specific member function signature, but the code below goes beyond and detects whether a given call is possible, no matter what is the signature. Hope the comments will be helpful.
#include <iostream>
template< class T >
class has_apply {
class yes { char c; };
class no { yes c[2]; };
struct mixin {
void apply( void );
};
// Calling derived::apply is only non-ambiguous if
// T::apply does not exist, cf. 10.2.2.
template< class U> struct derived : public U, public mixin {};
// The following template will help on deduction based on this fact.
// If U is type void (mixin::*) (void) then the template can be
// instantiated with u = &derived< U >::apply if and only if T::apply
// does not exist.
template< class U, U u >
class binder {};
// Therefore, the following template function is only selected if there
// is no T::apply:
template< class U >
static no deduce( U, binder< void (mixin::*) (void), &derived< U >::apply >* = 0 );
// Selected otherwise.
static yes deduce( ... );
// Provides an T object:
static T T_obj( void );
public:
static const bool result = ( sizeof( yes ) == sizeof( deduce( T_obj() ) ) );
};
namespace aux {
// Class to represent the void type as a "true" type.
class void_type {};
// deduce() some lines below will give us the right answer based on
// the return type of T::apply<>, but if it is void we cannot use a
// call to T::apply as an argument to deduce. In fact, the only
// function in c++ that can take such an argument is operator,() with
// its default behaviour and if an overload is not well formed it
// falls back to default.
template< class T >
T& operator,( const T&, void_type ) {};
// Copies the constness of T into U. This will be required in order
// to not get false positives when no const member is defined.
template< class T, class U >
struct copy_constness {
typedef U result;
};
template< class T, class U >
struct copy_constness< const T, U > {
typedef const U result;
};
}
template< class T >
class has_correct_apply{
class yes { char c; };
class no { yes c[2]; };
// We assume has_apply< T >::result is true so the following class
// is well declared. It is declared in a way such that a call to
// derived::apply< n >() is always possible. This will be necessary
// later.
struct derived : public T {
using T::apply; // possible iff has_apply< T >::result == true
// This template function will be selected if the function call
// we wish is otherwise invalid.
template< unsigned n >
static no apply( ... );
};
// const_correct_derived will have the same constness than T.
typedef typename aux::copy_constness< T, derived >::result const_correct_derived;
// Provides a const correct derived object.
static const_correct_derived derived_obj( void );
// Only possible call was derived::apply: call is impossible for signature:
static no deduce( no );
// Since te returned value of it will most likely be
// ignored in our code (void must be always [almost, see next]
// ignored anyway), we return yes from this:
static yes deduce( ... );
// As we noticed, an overload of operator,() may make an exact match necessary.
// If we want this we could simply have used "no" instead of "yes" above and:
// static no deduce( aux::void_type );
public:
static const bool result = ( sizeof( yes ) == sizeof( deduce(
( derived_obj().template apply< 0u >( 0.0 ), aux::void_type() )
) ) );
// Note: Inteestingly enough, GCC does not detect an private subclass default
// constructor and so const_correct_derived() could be used instead of
// having a function derived_obj(), but I do not know if this behavoiur is
// standard or not.
};
struct C {
template< unsigned n >
int apply( double, unsigned m = 10 ) const;
private:
C();
};
struct D {
template< unsigned n >
int apply( const double& );
private:
D();
};
struct E : public C {
};
struct Without{};
#include "mp.h"
int main()
{
std::cout << has_apply< E >::result << '\n';
std::cout << has_correct_apply< const E >::result << '\n';
std::cout << has_correct_apply< const D >::result << '\n';
std::cout << has_correct_apply< D >::result << '\n';
// E e;
return( 0 );
}