Logger<int, const char*>
任意のクラスのメンバー関数または非メンバー関数のいずれかを呼び出す単一の型を定義することができます。これを行うには、Class
パラメータを削除し、代わりに不透明オブジェクト ポインタ [ ] と不透明オブジェクトを受け入れる関数ポインタ[ ] をLogger
格納する必要があります。void*
R (*func)(void* object, A a)
Logger
これにより、質問に含まれる機能の種類がわからなくなるため、質問の問題が解決されます。それが非メンバー、クラスX
のメンバー、またはクラスのメンバーであるかどうかY
。
これは、私が C++03 用に開発した手法を使用して実装できます。この手法では、ラッパー関数 (別名「サンク」) を生成して、コンパイル時に既知の関数ポインターを介してメンバー関数と非メンバー関数を呼び出します。std::function
これは、C++11のカットダウン特殊バージョンまたはC# のデリゲートと考えることができます。
template<typename F>
struct FunctionTraits;
template<typename R, typename C, typename A>
struct FunctionTraits<R (C::*)(A)> // matches a pointer to member function
{
typedef R RetType;
typedef C Class;
typedef A Arg1Type;
};
template<typename R, typename A>
struct FunctionTraits<R (*)(A)> // matches a pointer to function
{
typedef R RetType;
typedef A Arg1Type;
};
template<typename RetType, typename Arg1Type>
class Logger
{
typedef RetType(*Func)(void*, Arg1Type);
public:
Logger(void* pars, Func func) : pars(pars), func(func)
{
}
RetType operator()(Arg1Type a) const
{
// call the function with the opaque object
return func(pars, a);
}
private:
Func func; // a pointer to a function accepting an opaque object
void* pars; // a pointer to an opaque object
};
template<typename F, F p>
typename FunctionTraits<F>::RetType callMember(void* c, typename FunctionTraits<F>::Arg1Type a)
{
// restore the type of the object
return (static_cast<typename FunctionTraits<F>::Class*>(c)->*p)(a);
}
template<typename F, F p>
Logger<typename FunctionTraits<F>::RetType, typename FunctionTraits<F>::Arg1Type>
makeLogger(typename FunctionTraits<F>::Class* pars)
{
typedef typename FunctionTraits<F>::RetType RetType;
typedef typename FunctionTraits<F>::Arg1Type Arg1Type;
// generates a 'thunk' function which calls the member 'p'
return Logger<RetType, Arg1Type>(pars, &callMember<F, p>);
}
template<typename F, F p>
typename FunctionTraits<F>::RetType callNonMember(void*, typename FunctionTraits<F>::Arg1Type a)
{
// the first parameter is not used
return (p)(a);
}
template<typename F, F p>
Logger<typename FunctionTraits<F>::RetType, typename FunctionTraits<F>::Arg1Type>
makeLogger()
{
typedef typename FunctionTraits<F>::RetType RetType;
typedef typename FunctionTraits<F>::Arg1Type Arg1Type;
// generates a 'thunk' function which calls the non-member 'p'
return Logger<RetType, Arg1Type>(0, &callNonMember<F, p>);
}
int log(const char*);
struct Parser
{
int log(const char*);
};
struct OtherParser
{
int log(const char*);
};
int main()
{
Logger<int, const char*> nonmember = makeLogger<decltype(&log), &log>();
int result1 = nonmember("nonmember"); // calls log("nonmember");
Parser pars;
Logger<int, const char*> member = makeLogger<decltype(&Parser::log), &Parser::log>(&pars);
int result2 = member("member"); // calls pars.log("member");
OtherParser other;
Logger<int, const char*> member2 = makeLogger<decltype(&OtherParser::log), &OtherParser::log>(&other);
int result3 = member2("member2"); // calls other.log("member2");
}
を使用しているにもかかわらずvoid*
、この手法はタイプ セーフであり、標準に準拠しています。
とは対照的にstd::function
、生成された関数は、コンパイル時にポインターが認識されるため、メンバー/非メンバー ポインターを介して呼び出しをインライン化できます。
編集:
上記の例では、C++11 の decltype を使用して関数ポインターの型を自動的に決定していますが、これは必須ではありません。同じことを実現する C++98 互換の手法を提供できます。
template<typename F>
struct NonMemberHelper
{
template<F p>
static Logger<typename FunctionTraits<F>::RetType, typename FunctionTraits<F>::Arg1Type>
apply()
{
return makeLogger<F, p>();
}
};
template<typename F>
NonMemberHelper<F> makeNonMemberHelper(F)
{
return NonMemberHelper<F>();
}
template<typename F>
struct MemberHelper
{
template<F p>
static Logger<typename FunctionTraits<F>::RetType, typename FunctionTraits<F>::Arg1Type>
apply(typename FunctionTraits<F>::Class* pars)
{
return makeLogger<F, p>(pars);
}
};
template<typename F>
MemberHelper<F> makeMemberHelper(F)
{
return MemberHelper<F>();
}
#define MAKE_LOGGER_NONMEMBER(func) makeNonMemberHelper(func).apply<(func)>()
#define MAKE_LOGGER(func, pars) makeMemberHelper(func).apply<(func)>(pars)
int main()
{
Logger<int, const char*> callNonMember = MAKE_LOGGER_NONMEMBER(&log);
int result1 = callNonMember("nonmember"); // calls log("nonmember");
Parser pars;
Logger<int, const char*> callMember = MAKE_LOGGER(&Parser::log, &pars);
int result2 = callMember("member"); // calls pars.log("member");
}