我花了最近几天尝试在C ++中为函数指针创建一个通用的包装器,我设法解决了几乎每一个问题。我的主要目标是能够简单地将对象作为函数调用内部存储的函数指针。如果指针指向某处,那么它会将其称为正常,而空指针只是不会调用该函数,它会继续,就好像什么都没发生一样。我打算主要用于回调函数,我可能不关心函数是否被调用,只是想要执行一个动作。它与以下几乎完美配合:
template<typename T>
class Action;
template<typename TReturn, typename ... TArgs>
class Action<TReturn(TArgs...)> {
public:
//! Define a type for the Action object
typedef TReturn(*signature)(TArgs...);
//! Constructors
inline Action(const signature& pFunc = nullptr) : mPointer(pFunc) {}
inline Action(const Action& pCopy) : mPointer(pCopy.mPointer) {}
//! Operator Call
inline bool operator() (TReturn& pReturn, TArgs ... pArgs) const { if (!mPointer) return false; pReturn = mPointer(pArgs...); return true; }
//! Operators
inline Action& operator=(const Action& pCopy) { mPointer = pCopy.mPointer; return *this; }
inline Action& operator=(const signature& pFunc) { mPointer = pFunc; return *this; }
inline operator bool() const { return (mPointer != nullptr); }
private:
//! Store a pointer to the callback function
signature mPointer;
};
template<typename ... TArgs>
class Action<void(TArgs...)> {
public:
//! Define a type for the Action object
typedef void(*signature)(TArgs...);
//! Constructors
inline Action(const signature& pFunc = nullptr) : mPointer(pFunc) {}
inline Action(const Action& pCopy) : mPointer(pCopy.mPointer) {}
//! Operator Call
inline bool operator() (TArgs ... pArgs) const { if (!mPointer) return false; mPointer(pArgs...); return true; }
//! Operators
inline Action& operator=(const Action& pCopy) { mPointer = pCopy.mPointer; return *this; }
inline Action& operator=(const signature& pFunc) { mPointer = pFunc; return *this; }
inline operator bool() const { return (mPointer != nullptr); }
private:
//! Store a pointer to the callback function
signature mPointer;
};
但是,我觉得最有可能使用此包装器的情况是调试信息或格式化文本的输出。这可以通过用户定义的函数或内置函数(如printf)实现。为了与printf的签名匹配,将创建一个Action,如:
Action<int(const char*, ...)> callback = printf;
它将能够以与任何其他Action行为相同的方式运行。我发现的问题是&#39; ...&#39;将强制模板签名不与任何一个特化对齐,而是与第一个仅仅是原型一起。
我完全理解为什么这不起作用以及为什么编译器无法处理所需类的生成但是我希望这里有人知道任何偷偷摸摸的方法来实现这个或类似的东西。任何帮助将不胜感激,谢谢:))
答案 0 :(得分:1)
以下示例适用于所有函数类型,以及没有捕获的lambdas:
#include <utility>
#include <cstdio>
#include <cmath>
template<typename Fn>
class Action {
Fn* function_ptr;
public:
Action() noexcept : function_ptr(nullptr) {}
Action(std::nullptr_t) noexcept : function_ptr(nullptr) {}
Action(const Action& other) : function_ptr(other.function_ptr) {}
Action(Fn f) : function_ptr(f) {}
Action& operator=(const Action& other) {
return (function_ptr = other.function_ptr, *this);
}
Action& operator=(std::nullptr_t ) {
return (function_ptr = nullptr, *this);
}
Action& operator=(Fn f) {
return (function_ptr = f, *this);
}
template<typename... Params>
auto operator()(Params&&... params) {
return function_ptr(std::forward<Params>(params)...);
}
};
正如您提出的问题所述,使用std::function
比编写函数指针的包装更好。 std::function
的唯一问题是它不能与包含省略号的函数签名一起使用。如果您明确指定了可以存储的签名,例如printf
如下:
std::function<int(const char*, int, double, double)> fn = printf;
如果你使用C ++ 17或Boost,你可以使用std::any
或Boost.Any来实现你自己的printf
- 类似功能,可以将std::function
分配给#include <iostream>
#include <string>
#include <vector>
#include <any>
#include <functional>
using namespace std;
void any_printf(string&& format, vector<any>&& args) {
int arg_index = 0; enum { NORMAL, CONVERT } mode;
for(auto& chr : format) {
if(mode == CONVERT) {
switch(chr) {
case 'd': cout << any_cast<int>(args[arg_index++]); break;
case 'f': cout << any_cast<float>(args[arg_index++]); break;
case 's': cout << any_cast<string>(args[arg_index++]); break;
/* ... */
default: cout << chr;
};
mode = NORMAL;
}
else {
chr == '%' ? (mode = CONVERT, 0) : (cout << chr, 0);
}
}
}
int main() {
using namespace string_literals;
function<void(string&&, vector<any>&&)> f_ptr { any_printf };
f_ptr("Cuboid: %smm x %dmm x %fmm.\n", { any("3"s), any(4), any(6.67f) });
return 0;
}
如下:
func textFieldDidBeginEditing(_ textField: UITextField) {
textField.returnKeyType = UIReturnKeyType.done
save_button.isEnabled = false
if textField == self.m_unit {
textField.resignFirstResponder()
self.drop_down.isHidden = false
}