可以定义一个Logger<int, const char*>
类型,它可以调用任何类的成员或非成员函数。为此,您需要从中删除Class
参数Logger
,而是存储一个不透明对象指针 [ void*
] 和一个接受不透明对象 [ R (*func)(void* object, A a)
] 的函数指针。
Logger
这通过不知道它包含什么样的功能来解决您的问题;无论是非成员、类X
成员还是类成员Y
。
您可以使用我为 C++03 开发的技术来实现这一点,该技术涉及生成包装函数(又名“thunk”)以通过编译时已知的函数指针调用成员函数和非成员函数。您可以将其视为std::function
C++11 或C# 中的Delegate的精简专用版本。
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");
}