I have some 3rdParty library with a method like this:
bool Invoke(const char* method, Value* args, size_t nargs)
It takes an array of its inner type (convertible to any primitive c++ types) and arg count as its inner params.
In my code, I wrote some generalized helper to avoid manual creation and type convertion for each invoke:
template<class ... Args>
bool WrappedValue::Invoke(const char* method, Args&& ... args)
{
3rdParty::Value values[] =
{
3rdParty::Value(std::forward<Args>(args))...
}
return m_value.Invoke(method, values, sizeof ... (Args));
}
It works just fine, but now I should have 3rdParty code defined in my header files and lib connected directly to my main project.
Is it possible to hide implementation details and usage of this 3rd party library? (Use some kind of pimple idiom or proxy object for 3rdParty::Value ). I know that it's not possible to use virtual template methods in c++ to create a proxy or simply move template implementation to .cpp, so I am totally stuck with this problem.
Will be grateful for any help)
Sure. Simply write the equivalent of std::variant<int, double, char, every, other, primitive, type>.
Now your Invoke converts your args into an array (vector, span, whatever) of those variants.
Then you pass this array of variants to your internal Invoke method.
That internal invoke method then uses the equivalent of std::visit to generate a 3rdParty::Value from each of your variants.
Boost provides a boost::variant which would probably work.
You could also roll this by hand. By narrowly specifying your problem, you'd get away with something simpler than a std::variant. It would be more than a bit of work, however.
Another approach is this
template<class T> struct tag_t {constexpr tag_t(){}; using type=T;};
template<class T> constexpr tag_t<T> tag{};
template<class T, class F, class ... Args>
bool WrappedValue::Invoke(tag_t<T>, F&& f, const char* method, Args&& ... args)
{
T values[] = {
T(std::forward<Args>(args))...
};
return std::forward<F>(f)(method, values, sizeof...(Args));
}
which is simpler. Here you'd write:
bool r = Invoke( tag<3rdParty::Value>, [&](const char* method, 3rdParty::Value* values, std::size_t count) {
m_value.Invoke( method, values, count );
}, 3.14, 42, "hello world");
If you want to avoid exposing the 3rdParty API, you need some non-template method to pass the data. That inevitably would require some type-erasure mechanism (like std::any), which instead is exposed in your API.
So, yes you could do that, but then the 3rdParty Value is already a type erasure method and this would only pass the data from one type erasure to the next, creating additional overhead. Whether that price is worth paying only you can decide.
I somehow overlooked your remark that the arguments are all primitive. In this case, type erasure is much simpler and can be done via a tag+union like
struct erasure_of_primitive
{
enum { is_void=0, is_str=1, is_int=2, is_flt=3, is_ptr=4 }
int type = is_void;
union {
const char*s; // pointer to external C-string
int64_t i; // any integer
double d; // any floating point number
void*p; // any pointer
} u;
erasure_of_primitive() = default;
erasure_of_primitive(erasure_of_primitive&const) = default;
erasure_of_primitive&operator=(erasure_of_primitive&const) = default;
erasure_of_primitive(const char*str)
: type(is_str), u.s(str) {}
template<typename T>
erasure_of_primitive(T x, enable_if_t<is_integer<T>::value>* =0)
: type(is_int), u.i(x) {}
template<typename T>
erasure_of_primitive(T x, enable_if_t<is_floating_point<T>::value>* =0)
: type(is_flt), u.d(x) {}
template<typename T>
erasure_of_primitive(T*x)
: type(is_ptr), u.p(static_cast<void*>(x)) {}
};
Related
Is there a way to apply the function-signature-as-template-parameter unpacking idiom in a way that works with C++/CLI managed types?
As an example, consider the following code:
#include <msclr/gcroot.h>
using namespace System;
template<typename... Args>
ref struct ManagedDelegate abstract
{
delegate void Fn(Args...);
};
template<typename Signature>
struct Method;
template<typename... Args>
struct Method<void(Args...)>
{
using Fn = typename ManagedDelegate<Args...>::Fn;
Method(Fn^ m) : m_Method(m) {}
void operator()(Args... args)
{
auto method = safe_cast<Fn^>(m_Method);
method(args...);
}
private:
msclr::gcroot<Fn^> m_Method;
};
void f1(int a, int b)
{
Console::WriteLine("a = {0}, b = {1}", a, b);
}
void f2(String^ s)
{
Console::WriteLine("s = {0}", s);
}
int main(array<String ^> ^args)
{
using Method1 = Method<void(int, int)>;
Method1 m1(gcnew Method1::Fn(&f1));
m1(4, 5);
using Method2 = Method<void(String^)>;
Method2 m2(gcnew Method2::Fn(&f2));
m2("hello world");
return 0;
}
(The separate ManagedDelegate is a little annoying, but it's not possible to declare a delegate type inside a native class, sadly.)
If you comment out all the Method2 code at the bottom, then this compiles and runs as you'd expect -- it calls f1(4, 5) and prints accordingly.
Trying to do the same thing with a managed type argument, however, causes the template to fail to match the specialisation and results in:
error C2027: use of undefined type 'Method<void (System::String ^)>'
Is this a compiler bug, or is there some way to get this to work? There are some constraints that I do need to keep to in order for this to work in my real code:
Method needs to be an unmanaged type that contains a gcroot of the delegate type.
The use of templates rather than generics is intended. I don't think any of this is possible with generics anyway.
The non-use of std::forward is also intended, since this also upsets managed types. (And I'm not intending to pass native reference arguments anyway, so it's unnecessary.)
While I prefer automatically creating the delegate type from the signature as shown here, it would also be acceptable to create the delegate outside and pass it in instead of a signature, eg:
delegate void Method1Delegate(int, int);
...
Method<Method1Delegate> m1(gcnew Method1Delegate(&f1));
But either way, I do need an Args... parameter list (both for the operator() and for other reasons). And I don't think it's possible to extract this from a managed delegate type.
I also want the operator() to keep using Args... from the Method type so that it won't accept the "wrong" parameters. (I did have an older version of the code that templated Args directly on operator(), but this gives IntelliSense the false impression that it would accept any parameters.)
If there is a way to do the above, then I'd probably want a version that works with a templated return type as well as just void. I know how to do that with the above code -- just that any rewrite shouldn't prevent that working if possible.
EDIT: as demonstration that the managed args sort of work in variadics, this can be added:
template<>
struct Method<void(String^)>
{
using Fn = typename ManagedDelegate<String^>::Fn;
Method(Fn^ m) : m_Method(m) {}
template<typename... Args>
void operator()(Args... args)
{
auto method = safe_cast<Fn^>(m_Method);
method(args...);
}
private:
msclr::gcroot<Fn^> m_Method;
};
This works, provided that the call is changed to m2(gcnew String("hello world")); to force the correct type, or operator() is changed to accept a single String^ parameter instead of an open variadic. So the problem is definitely in matching a variadic template specialisation, not elsewhere.
I can mostly do what I want by abandoning the function-signature-specialisation and just specifying the signature components separately:
template<typename R, typename... Args>
ref struct ManagedDelegate abstract
{
delegate R Fn(Args...);
};
template<typename R, typename... Args>
struct Method
{
using Fn = typename ManagedDelegate<R, Args...>::Fn;
Method(Fn^ m) : m_Method(m) {}
R operator()(Args... args)
{
auto method = safe_cast<Fn^>(m_Method);
return method(args...);
}
private:
msclr::gcroot<Fn^> m_Method;
};
//...
using Method2 = Method<void, String^>;
Method2 m2(gcnew Method2::Fn(&f2));
m2("hello world");
This is not ideal, but it does compile and work. I'm still interested in any alternative answer that does support unpacking a function signature type, however. (And I filed the original issue as a compiler bug.)
As I understand, typedef cannot be used for overloading but what if I need to use some different types as arguments to the function pointer?
How can I make it work with the following functionality?
{
public:
typedef void (*InitFunc)(float x);
typedef void (*InitFunc)(int a, char b); //Needs to be added
virtual void initialize(InitFunc init) = 0;
};
Edit:
I cannot use C++17, so can't use variant
As commented, the easiest way is a union, although not very type safe and C++-y. Here is an example with inheritance, since you commented that you want inheritance.
typedef void (*FloatInit)(float x);
typedef void (*IntCharInit)(int a, char b);
union InitFn {
FloatInit fi;
IntCharInit ici;
};
struct Foo {
void initialize(InitFn) = 0;
};
struct FloatFoo: public Foo {
void initialize(InitFn f) override {
f.fi(42.0f);
}
};
void test(float) {}
// ...
auto x = FloatFoo{};
x.initialize(InitFn{test});
As mentioned by other commenters, you can use std::variant to enhance type safety and get rid of the manual union definition:
typedef void (*FloatInit)(float x);
typedef void (*IntCharInit)(int a, char b);
typedef std::variant<FloatInit, IntCharInit> InitFn;
struct Foo {
void initialize(InitFn) = 0;
};
struct FloatFoo: public Foo {
void initialize(InitFn f) override {
std::get<FloatInit>(f)(42.0f);
}
};
void test(float) {}
// ...
auto x = FloatFoo{};
x.initialize(InitFn{test});
One solution is to create a simple wrapper class template instead, to allow the compiler to automatically generate instantiations as necessary. This is relatively simple if init is always guaranteed to be a non-member function (and by extension, an actual function and not a functor/lambda).
// Quick-and-dirty transparent callable wrapper, to serve as overloadable "type alias".
template<typename>
class InitFunc;
template<typename Ret, typename... Params>
class InitFunc<Ret(*)(Params...)> {
public:
// Supply component types if needed.
// Tuple used for params, for convenience.
using return_type = Ret;
using param_types = std::tuple<Params...>;
using func_type = Ret(Params...);
using func_ptr_type = func_type*;
using func_ref_type = func_type&;
// Create from pointer or reference.
constexpr InitFunc(func_ptr_type p = nullptr) : ptr(p) {}
constexpr InitFunc(func_ref_type r) : ptr(&r) {}
// Transparent invocation.
// Deduces argument types instead of relying on Params, to allow for perfect forwarding.
template<typename... Ts>
constexpr return_type operator()(Ts&&... ts) { return ptr(std::forward<Ts>(ts)...); }
// Convert back to original type if necessary.
operator func_ptr_type() { return ptr; }
operator func_ref_type() { return *ptr; }
private:
// Actual function pointer.
func_ptr_type ptr;
};
// And a nice, clean creator, which can be renamed as necessary.
template<typename Init>
constexpr auto make(Init func) { return InitFunc<Init>(func); }
This creates a nice little wrapper that can easily be optimised out entirely, and will compile as long as C++14 support is available.
Note that you require a C++11 compiler (or variadic templates, rvalue references, perfect forwarding, and constexpr support) at the absolute minimum, and will need to modify make() to have a trailing return type for pre-C++14 compilers. I believe this is compatible with C++11 constexpr, but I'm not 100% sure.
If you want InitFunc to be able to accept pointers/references-to-member-function (including functors and lambdas), you'll need to provide an additional version to isolate it into a non-member "function", and likely bind it to a class instance. It may be worth looking into std::bind() in this case, although I'm not sure if it has any overhead.
In this case, I would suggest splitting the member types off into a base class, to reduce the amount of code you'll need to duplicate.
// Quick-and-dirty transparent callable wrapper, to serve as overloadable "type alias".
template<typename>
class InitFunc;
// Supply component types if needed.
// Tuple used for params, for convenience.
// Using actual function type as a base, similar to std::function.
template<typename Ret, typename... Params>
class InitFunc<Ret(Params...)> {
public:
using return_type = Ret;
using param_types = std::tuple<Params...>;
using func_type = Ret(Params...);
using func_ptr_type = func_type*;
using func_ref_type = func_type&;
};
// Non-member functions.
// As member types are now dependent types, we qualify them and use `typename`.
// Yes, it looks just as silly as you think it does.
template<typename Ret, typename... Params>
class InitFunc<Ret(*)(Params...)> : public InitFunc<Ret(Params...)> {
// Actual function pointer.
typename InitFunc::func_ptr_type ptr;
public:
// Create from pointer or reference.
constexpr InitFunc(typename InitFunc::func_ptr_type p = nullptr) : ptr(p) {}
constexpr InitFunc(typename InitFunc::func_ref_type r) : ptr(&r) {}
// Transparent invocation.
// Deduces argument types instead of relying on Params, to allow for perfect forwarding.
template<typename... Ts>
constexpr typename InitFunc::return_type operator()(Ts&&... ts) { return ptr(std::forward<Ts>(ts)...); }
// Convert back to original type if necessary.
operator typename InitFunc::func_ptr_type() { return ptr; }
operator typename InitFunc::func_ref_type() { return *ptr; }
};
// See ecatmur's http://stackoverflow.com/a/13359520/5386374 for how to accomodate member functions.
// ...
// Non-member function make() is unaffected.
// An overload will likely be needed for member functions.
template<typename Init>
auto make(Init func) { return InitFunc<Init>(func); }
Despite the awkwardness inside our derived specialisation, any code that relies on InitFunc shouldn't (to my knowledge) see any changes to its API; the previous example will work just fine if we swap to this new InitFunc, and be none the wiser after recompilation.
Note that it will change the ABI, though, and thus any code compiled for the simpler InitFunc will need to be recompiled for this version.
Is it even possible to express a sort of monad" C++ ?
I started to write something like this, but stuck:
#include <iostream>
template <typename a, typename b> struct M;
template <typename a, typename b> struct M {
virtual M<b>& operator>>( M<b>& (*fn)(M<a> &m, const a &x) ) = 0;
};
template <typename a, typename b>
struct MSome : public M<a> {
virtual M<b>& operator>>( M<a>& (*fn)(M<a> &m, const a &x) ) {
return fn(*this, x);
}
private:
a x;
};
M<int, int>& wtf(M<int> &m, const int &v) {
std::cout << v << std::endl;
return m;
}
int main() {
// MSome<int> v;
// v >> wtf >> wtf;
return 0;
}
but faced the lack of polymorphism. Actually it may be my uncurrent C++ 'cause I used it last time 8 years ago. May be it possible to express general monadic interface using some new C++ features, like type inference. It's just for fun and for explaining monads to non-haskellers and non-mathematicians.
C++' type system is not powerful enough to abstract over higher-kinded types, but since templates are duck-typed you may ignore this and just implement various Monads seperately and then express the monadic operations as SFINAE templates. Ugly, but the best it gets.
This comment is bang on the money. Time and time again I see people trying to make template specializations 'covariant' and/or abuse inheritance. For better or for worse, concept-oriented generic programming is in my opinion* saner. Here's a quick demo that will use C++11 features for brevity and clarity, although it should be possible to implement the same functionality in C++03:
(*: for a competing opinion, refer to "Ugly, but the best it gets" in my quote!)
#include <utility>
#include <type_traits>
// SFINAE utility
template<typename...> struct void_ { using type = void; };
template<typename... T> using Void = typename void_<T...>::type;
/*
* In an ideal world std::result_of would just work instead of all that.
* Consider this as a write-once (until std::result_of is fixed), use-many
* situation.
*/
template<typename Sig, typename Sfinae = void> struct result_of {};
template<typename F, typename... Args>
struct result_of<
F(Args...)
, Void<decltype(std::declval<F>()(std::declval<Args>()...))>
> {
using type = decltype(std::declval<F>()(std::declval<Args>()...));
};
template<typename Sig> using ResultOf = typename result_of<Sig>::type;
/*
* Note how both template parameters have kind *, MonadicValue would be
* m a, not m. We don't whether MonadicValue is a specialization of some M<T>
* or not (or derived from a specialization of some M<T>). Note that it is
* possible to retrieve the a in m a via typename MonadicValue::value_type
* if MonadicValue is indeed a model of the proper concept.
*
* Defer actual implementation to the operator() of MonadicValue,
* which will do the monad-specific operation
*/
template<
typename MonadicValue
, typename F
/* It is possible to put a self-documenting assertion here
that will *not* SFINAE out but truly result in a hard error
unless some conditions are not satisfied -- I leave this out
for brevity
, Requires<
MonadicValueConcept<MonadicValue>
// The two following constraints ensure that
// F has signature a -> m b
, Callable<F, ValueType<MonadicValue>>
, MonadicValueConcept<ResultOf<F(ValueType<MonadicValue>)>>
>...
*/
>
ResultOf<MonadicValue(F)>
bind(MonadicValue&& value, F&& f)
{ return std::forward<MonadicValue>(value)(std::forward<F>(f)); }
// Picking Maybe as an example monad because it's easy
template<typename T>
struct just_type {
using value_type = T;
// Encapsulation omitted for brevity
value_type value;
template<typename F>
// The use of ResultOf means that we have a soft contraint
// here, but the commented Requires clause in bind happens
// before we would end up here
ResultOf<F(value_type)>
operator()(F&& f)
{ return std::forward<F>(f)(value); }
};
template<typename T>
just_type<T> just(T&& t)
{ return { std::forward<T>(t) }; }
template<typename T>
just_type<typename std::decay<T>::type> make_just(T&& t)
{ return { std::forward<T>(t) }; }
struct nothing_type {
// Note that because nothing_type and just_type<T>
// are part of the same concept we *must* put in
// a value_type member type -- whether you need
// a value member or not however is a design
// consideration with trade-offs
struct universal { template<typename T> operator T(); };
using value_type = universal;
template<typename F>
nothing_type operator()(F const&) const
{ return {}; }
};
constexpr nothing_type nothing;
It is then possible to write something like bind(bind(make_just(6), [](int i) { return i - 2; }), [](int i) { return just("Hello, World!"[i]); }). Be aware that the code in this post is incomplete in that the wrapped values aren't forwarded properly, there should be errors as soon as const-qualified and move-only types are involved. You can see the code in action (with GCC 4.7) here, although that might be a misnomer as all it does is not trigger assertions. (Same code on ideone for future readers.)
The core of the solution is that none of just_type<T>, nothing_type or MonadicValue (inside bind) are monads, but are types for some monadic values of an overarching monad -- just_type<int> and nothing_type together are a monad (sort of -- I'm putting aside the matter of kind right now, but keep in mind that it's possible to e.g. rebind template specializations after the fact, like for std::allocator<T>!). As such bind has to be somewhat lenient in what it accepts, but notice how that doesn't mean it must accept everything.
It is of course perfectly possible to have a class template M such that M<T> is a model of MonadicValue and bind(m, f) only ever has type M<U> where m has type M<T>. This would in a sense make M the monad (with kind * -> *), and the code would still work. (And speaking of Maybe, perhaps adapting boost::optional<T> to have a monadic interface would be a good exercise.)
The astute reader would have noticed that I don't have an equivalent of return here, everything is done with the just and make_just factories which are the counterparts to the Just constructor. This is to keep the answer short -- a possible solution would be to write a pure that does the job of return, and that returns a value that is implicitly convertible to any type that models MonadicValue (by deferring for instance to some MonadicValue::pure).
There are design considerations though in that the limited type deduction of C++ means that bind(pure(4), [](int) { return pure(5); }) would not work out of the box. It is not, however, an insurmountable problem. (Some outlines of a solution are to overload bind, but that's inconvenient if we add to the interface of our MonadicValue concept since any new operation must also be able to deal with pure values explicitly; or to make a pure value a model of MonadicValue as well.)
I would do it like this:
template<class T>
class IO {
public:
virtual T get() const=0;
};
template<class T, class K>
class C : public IO<K> {
public:
C(IO<T> &io1, IO<K> &io2) : io1(io1), io2(io2) { }
K get() const {
io1.get();
return io2.get();
}
private:
IO<T> &io1;
IO<K> &io2;
};
int main() {
IO<float> *io = new YYYY;
IO<int> *io2 = new XXX;
C<float,int> c(*io, *io2);
return c.get();
}
I just dont know how to do it..
Basically, if each template specialization type (T) have different parameters for its initialization, how can a generalized ResourceCache load/create a resource?
template< class T>
class ResourceCache{
T* cache[100];
T* LoadResource(different parameters for different T ){//used in a cache miss..
Create( different parameters for different T );
}
}
If I use abstraction for, i.e., a IResourceParams class, my ResourceCache will not be able to use its own known type resource data without using polymorphism, its kinda stupid, since at runtime he knows what the type is, id be tottaly doing shit at runtime in prol of a compile time facility...I guess..
On my current try I created a templated IResourceDesc that have a virtual T* Create() method, so you need to derive for add data and specialize the Create method, but it sucks, because I cant have a collection of IResourceDesc in the ResourceCache class ( for comparing current loaded ones, acquiring cached resources by desc, etc)...
In C++11, this is rather easy with a variadic template and perfect-forwarding:
#include <utility>
template<class... Args>
T* LoadResource(Args&&... args){
unsigned dest_index = /* pick it */ 0;
cache[dest_index] = new T(std::forward<Args>(args)...);
return cache[dest_index];
}
For C++03, either provide ~10 overloads with different number of parameters or go for the in-place factory style:
template< class T>
class ResourceCache{
T* cache[100];
template<class Factory>
T* LoadResource(Factory const& f){
unsigned dest_index = /* pick cache slot */ 0;
void* dest = operator new(sizeof(T));
cache[dest_index] = f.construct(dest);
return cache[dest_index];
}
}
template<class T, class A1>
struct in_place_factory1{
in_place_factory1(A1 const& a1) : _arg1(a1) {}
int* construct(void* dest) const{
return new (dest) T(_arg1);
}
private:
A1 const& _arg1; // make sure the original argument outlives the factory
};
// in code
ResourceCache<int> cache;
int arg = 5;
int* res = cache.LoadResource(in_place_factory1<int,int>(arg));
In-place factories are basically an inferior version of perfect-forwarding variadic template functions that can emplace the object directly into the containers storage without requiring an already complete object for a copy.
I have a class parameterised by some template parameters:
template<typename Scalar, typename Integrator, int Dimension>
class foo;
Each of the template parameters can be one of a few possible types. Currently the type of foo used is hard-coded in man typedef foo<...> foo_type. I wish to adapt my program so that a collection of foo's are supported; something like:
if (desired_foo_str == "2DSimpleFloat")
{
foo<float,Simple,2>(params).method();
}
else if (desired_foo_str == "3DSimpleDouble")
{
foo<double,Simple,3>(params).method();
}
else
{
std::cout << "Unsupported foo."
}
The interface of foo does not depend on its template parameters. My question is how can I improve this solution? I know boost::mpl provides a type vector but it seems more for compile time reductions as opposed to run-time switching.
Clarification
Lets say (this is a simplification) that my program takes a set of points in N-dimensions (provided by the user) and integrates them. Certain combinations of dimensions, integration methods and scalar types can be accelerated by SIMD (hence the use of template parameters). All combinations of foo<A,B,N> are valid however different users (all of whom will have compiled my program) will require only a couple of specific specializations for their work. I wish to allow for:
$ integrate --method=2DSimpleFloat mypoints2d.dat
$ integrate --methid=3DSimpleDouble mypoints3d.dat
so run-time selection of what method they wish to use. I am wondering what kind of frame-work best allows me to associate types with strings such that I can better handle the above scenario.
You could make templated default method which throws an error, and template-specializations per combination that you support.
class Simple {};
template<typename Scalar, typename Integrator, int Dimension>
class foo
{
public:
void method();
foo() {}
};
// default implementation throws an error
template<typename Scalar, typename Integrator, int Dimension>
void foo<Scalar,Integrator,Dimension>::method() { cout << "unsupported\n"; };
// override default for supported cases:-
template<>
void foo<double,Simple,2>::method() { cout <<"method1\n"; };
template<>
void foo<double,Simple,3>::method() { cout <<"method2\n"; };
// test program
void main() {
foo<float,Simple,2> a; a.method(); // output "unsupported"
foo<double,Simple,2> b; b.method(); // output "method1"
foo<double,Simple,3> c; c.method(); // output "method2"
}
You should be able to mix general purpose implementations and special purpose overides freely throughout the class; (e.g. perhaps some permeation can be handled with SIMD intrinsics or whatever)
If all the class methods were identical and generic, a convenient way to restrict use might be to restrict the constructor so that undesired cases can't be instantiated
in general if the mechanisms of overloading and templates are being used correctly, you should be able to avoid checking types manually where they're used.
This can all work compile time statically linked without any pointers or virtual dispatch.
If the supported implementations are to be the same, the over-rides can be wrappers to direct to another templated method as suggested above.
Your question doesn't provide enough information for a complete answer, but I have a hunch: Perhaps you should look into refactoring your code so as to separate the part that is independent of the parameters from the code that depends on the template parameters.
The typical example is taken from Scott Meyers's book. Suppose you have a square matrix multiplicator, and you write this as a full template:
template <typename T, unsigned int N>
Matrix<T, N> multiply(Matrix<T, N>, Matrix<T, N>)
{
// heavy code
}
With this setup, the compiler would generate a separate piece of code for each size value N! That's potentially a lot of code, and all that N provides is a bound in a loop.
So the suggestion here is to turn compile-time into runtime parameters and refactor the workload into a separate function, and only use template stubs to dispatch the call:
template <typename T>
void multiply_impl(unsigned int N,
MatrixBuf<T> const & in1, MatrixBuf<T> const & in1,
MatrixBuf<T> & out)
{
// heavy work
}
template <typename T, unsigned int N>
Matrix<T, N> multiply(Matrix<T, N> const & in1, Matrix<T, N> const & in1)
{
Matrix<T, N> out;
multiply_impl(N, in1.buf(), in2.buf(), out.buf());
}
You could do something similar: Put all the argument-independent code in a base class, and make the derived classes templates. The runtime can then use a factory function to create the correct concrete instance at runtime. As an alternative to inheritance you can also make a type-erasing wrapper class that contains a private pointer-to-base, and the runtime populates this with concrete derived implementation instances.
I'm guesing you are looking for register pattern. This is only my draft, so don't rely on it.
class AbstractFooFactory
{
virtual AbstractFoo* create( ParamsType cons& params ) = 0;
// or construct on stack and call .method()
virtual void createAndCallMethod( ParamsType cons& params ) = 0;
};
class FooRegister
{
~FooRegister(); // delete all pointers
template< typename FooFactory >
void operator() ( FooFactory const & factory ) // for boost::mpl:for_each
{ map[factory.getName()]= new FooFactory( factory ); }
AbstractFooFactory* get( std::string name );
std::map< std::string , AbstractFooFactory* > map;
};
template< typename Scalar, typename Integrator, typename Dimension >
class FooFactory: public AbstractFooFactory
{
typedef FooFactory<Scalar, Integrator, Dimension > type; // Metafunction
std::string getName(); // this will be a bit hard to implement
AbstractFoo* create( ParamsType cons& params );
void createAndCallMethod( ParamsType cons& params );
};
Simple trails may be used for storing type names:
template< typename Type >
struct NameTrails
{
static const char const* value;
};
template<> const char const* NameTrails<int>::value = "Int";
template<> const char const* NameTrails<float>::value = "Float";
template<> const char const* NameTrails<double>::value = "Double";
template<> const char const* NameTrails<Simple>::value = "Simple";
template<> const char const* NameTrails<Complex>::value = "Complex";
template< typename Scalar, typename Integrator, typename Dimension >
std::string FooFactory::getName()
{
return boost::lexical_cast<std::string>( Dimension::value ) + "D"
+ NameTrails< Integrator >::value
+ NameTrails< Scalar >::value;
}
And now you need to register all types using mpl::for_each:
FooRegister fooRegister;
typedef boost::mpl::vector<Simple,Complex> IntegratorsList;
typedef boost::mpl::vector<int,float,double> ScalarsList;
typedef boost::mpl::range_c<int,1,4> DimensionsList;
typedef boost::mpl::vector<
boost::mpl::vector< Simple, float, boost::mpl::int_<2> >,
boost::mpl::vector< Simple, double, boost::mpl::int_<3> >,
... other types or full cross join ... > FooList;
boost::mpl::for_each< FooList, boost::mpl::quote3<FooFactory> >(
boost::ref(fooRegister) );
What i don't know is how to cross join IntegratorsList, ScalarList, range_c<int,1,4> to constuct full FooList.
fooRegister.get("2DSimpleFloat")->createAndCallMethod(params);
You probably want to do this statically, so yes it is possible, but i find it rather difficult to achieve better performance then a simple dynamic map or hash map.