Oh, habe ich etwas vergessen gehabt. War wie gesagt aus dem Kopf heraus. Den Code ein wenig modifiziert:
#include <memory>
struct EmptyType { };
template
<
typename P0 = EmptyType,
typename P1 = EmptyType,
typename P2 = EmptyType
// usw. bis maximale Anzahle Parameter
>
struct Parameters
{
typedef P0 Type0;
typedef P1 Type1;
typedef P2 Type2;
// usw. bis maximale Anzahle Parameter
// Beide Listen kann man auch mit Makros bauen lassen,
// somit auch die ganze Klasse ...
};
template<typename T>
struct FunctorImpl;
template<typename R>
struct FunctorImpl<R()>
{
typedef R ResultType;
typedef Parameters<> Parameters;
virtual R call() const = 0;
virtual FunctorImpl* clone() const = 0;
};
template<typename R, typename P0>
struct FunctorImpl<R(P0)>
{
typedef R ResultType;
typedef Parameters<P0> Parameters;
virtual R call(P0) const = 0;
virtual FunctorImpl* clone() const = 0;
};
template<typename R, typename P0, typename P1>
struct FunctorImpl<R(P0, P1)>
{
typedef R ResultType;
typedef Parameters<P0, P1> Parameters;
virtual R call(P0, P1) const = 0;
virtual FunctorImpl* clone() const = 0;
};
// usw. halt mit Makros aktuell
template<typename ImplT, typename FuncT>
struct FunctorHandler
: ImplT
{
private:
FuncT m_func;
public:
FunctorHandler(FuncT func)
: m_func(func)
{
}
public:
typename ImplT::ResultType call() const
{
return m_func();
}
typename ImplT::ResultType call(typename ImplT::Parameters::Type0 p0) const
{
return m_func(p0);
}
typename ImplT::ResultType call(typename ImplT::Parameters::Type0 p0, typename ImplT::Parameters::Type1 p1) const
{
return m_func(p0, p1);
}
// usw. mit Makros
FunctorHandler* clone() const
{
return new FunctorHandler(*this);
}
};
template<typename T>
struct Functor
{
private:
typedef FunctorImpl<T> Impl;
std::auto_ptr<Impl> m_impl;
public:
template<typename FuncT>
Functor(FuncT func)
: m_impl(new FunctorHandler<Impl, FuncT>(func))
{
}
public:
typename Impl::ResultType operator ()() const
{
return m_impl->call();
}
typename Impl::ResultType operator ()(typename Impl::Parameters::Type0 p0) const
{
return m_impl->call(p0);
}
typename Impl::ResultType operator ()(typename Impl::Parameters::Type0 p0, typename Impl::Parameters::Type1 p1) const
{
return m_impl->call(p0, p1);
}
// usw. mit Makros
};
// ...
#include <iostream>
int foo()
{
return 0;
}
int main()
{
Functor<int()> functor(&foo);
std::cout << functor() << std::endl;
return 0;
}
Jetzt geht es auch mit weniger. Hmmm, die Verwendung von Typlisten wäre hier ziemlich von Vorteil, aber bin jetzt zu Faul dieses Zeug auch noch hinzuschreiben ...
Wesentlicher Unterschied zur vorherigen Version ist die Struktur Parameters . Problem war natürlich, dass nicht alle die maximale Parameter Zahl aufweisen, daher müssen diese mit dem EmptyType besetzt werden. Die Struktur Parameters dient zur Vereinfachung. Über eine Typliste hätte man das ganze wohl noch mehr vereinfachen können.
Die Makros selber sind nur wichtig, wenn du mehr als 2 Parameter aktzeptieren möchtest. Wie das dann funktioniert, habe ich schon weiter vorne beschrieben mit der print Funktion. Wenn du wirklich beide Vorgehen vollständig verstanden hast, solltest du in der Lage sein, diese zu kombininieren.
Aber ich glaube, ich empfehle hier langsam lieber das folgende Buch:
Modern C++ Design von Andrei Alexandrescu
Modern C++ Design | ISBN: 0201704315
Dort kommen solche Themen dran und werden ausführlich erklärt und nicht nur auf die Schnelle aus dem Kopf heraus
Grüssli