如何允许模板化函子在成员和非成员函数上工作
How to allow templated functor work on both member and non-member functions
我得到了这个日志记录模板化函子
template<typename RetType, typename Arg1Type, typename Class>
class Logger
{ public:
RetType operator()(Arg1Type s, ...)
{
if(func != 0 && parser != 0)
return (parser->*func)(s);
else if(nfunc != 0)
return nfunc(s);
return RetType();
}
Logger& operator=(RetType(*fun)(Arg1Type s, ...))
{
func = fun;
return *this;
}
void Bind(Class* pars, RetType(Class::*fun)(Arg1Type s,...))
{
parser = pars;
func = fun;
nfunc = 0;
}
void Bind(RetType(*fun)(Arg1Type s,...))
{
nfunc = fun;
func = 0;
parser = 0;
}
private:
RetType (Class::*func)(Arg1Type s, ...); //member class method
RetType(*nfunc)(Arg1Type s, ...); //non-member class method
Class* parser;
};
现在我可以使用这样的东西来调用这个类:
Logger<int, const char*, WinLogger > p1;
WinLogger w1;
p1.Bind(&w1, &WinParser::Log);
p1("log");
但是当我想使用以下方法将其绑定到任何非成员函数时:
Logger<int, const char*, void>
编译器抱怨:"类":后跟"::"时必须是类或命名空间。因为他无法将 void 类型适合第一个 Bind 方法。但是,如果使用任何傻瓜类创建记录器,则可以。
Logger<int, const char*, DummyClass> p2;
p2.Bind(printf);
p2("printf called");
这是非常丑陋的。有解决方法吗?
我知道我可能应该使用 boost::function 等,但我想确切地了解函子指针和函子的工作原理,所以我决定不使用它。
定义单个Logger<int, const char*>
类型,该类型可以调用任何类的成员或非成员函数。为此,您需要从Logger
中删除 Class
参数,而是存储一个不透明对象指针 [ void*
] 和一个接受不透明对象 [ R (*func)(void* object, A a)
] 的函数指针。
这解决了你问题中的问题,Logger
不知道它包含什么样的函数;无论是非成员、类X
的成员还是类Y
的成员。
您可以使用我为 C++03 开发的技术来实现这一点,该技术涉及生成包装函数(也称为"thunks"(以通过编译时已知的函数指针调用成员和非成员函数。您可以将其视为 C++11 中 std::function
或 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");
}
#include <iostream>
#include <type_traits>
template <typename T>
struct Logger
{
Logger(T func) : func(func)
{
}
template <typename... Args>
auto operator()(const Args&... params) -> decltype(std::declval<T>()(params...))
{
return func(params...);
}
T func;
};
void simple_logger(const char *mesg)
{
std::cout << "Simple: " << mesg << std::endl;
}
struct ComplexLogger
{
std::ostream& operator()(const char *mesg)
{
return std::cout << "Complex: " << mesg;
}
};
int main()
{
Logger<decltype(&simple_logger)> l1(simple_logger);
l1("hello!");
ComplexLogger cl;
Logger<ComplexLogger> l2(cl);
l2("hello!") << "yello!";
}
输出
Simple: hello!
Complex: hello!yello!
我在<algorithm>
中看到比较函数(BinaryPredicate
、UnaryPredicate
等(被当作模板化的参数,这样函子(例如 less
( 或函数指针可以作为参数传递。我在这里使用了类似的技巧。
我不知道它是否与您的情况完全匹配(因为它希望日志记录类具有operator()
定义,并且它使用 C++11 个功能(,尽管我认为这可能会有所帮助。
你肯定需要一个类的某种默认实现。但好消息是,记录器类本身可以预见这一点。只需更换
template<typename RetType, typename Arg1Type, typename Class>
跟
template<typename RetType, typename Arg1Type, typename Class=DefaultDummyClass>
当然,DefaultDummyClass 是可以预见的,但它不再需要由想要使用记录器类的人提供。
我建议学习boost::function。 不久前我写了一篇文章,可能会有所帮助:
http://crazyeddiecpp.blogspot.com/2010/02/implementing-stdtr1function-pt-1.html
不过,我从未回到任务中,也不讨论成员功能。 不过,它可能会帮助您入门。
请注意,boost使用不同的技术来做同样的事情。 他们使用 void 函数指针而不是继承来避免使用 RTTI 创建的所有数据以及所有这些数据。
- 对RValue对象调用的LValue ref限定成员函数
- 为什么使用 "this" 指针调用派生成员函数?
- 将公共但非静态的成员函数与ALGLIB集成
- 使用指向成员的指针将成员函数作为参数传递
- 将重载的成员函数传递给函数模板
- 我不小心调用了一个没有自己类对象的成员函数.但这是怎么回事呢
- 如何在C++中使用非静态成员函数作为回调函数
- C++错误C2600:无法定义编译器生成的特殊成员函数(必须首先在类中声明)
- 关联容器的下界复杂性:成员函数与非成员函数
- 在 C++ 中用派生类型重写成员函数
- 链表的泛型函数remove()与成员函数remove)
- 如何将lambda作为模板类的成员函数参数
- constexpr构造函数需要常量成员函数时出现问题
- 将自由函数绑定为类成员函数
- 区分非成员函数和头文件中的成员函数
- 如何从子成员函数修改父公共成员变量
- 保留对其他类的成员函数的引用
- 在运算符重载定义中使用成员函数(const错误)
- 内联如何影响模块接口中的成员函数
- 将成员函数指针作为参数传递给模板方法