我正在尝试绑定一些ta-lib函数,然后进行回调。
下面是简化的示例代码:
#include <functional>
#include <type_traits>
#include <cstdint>
struct DataChunk {
// ...
};
typedef uint64_t idx_t;
template <typename LookbackArgType> // int, double
struct talib_traits {
using talib_lookback_t = std::function<int(LookbackArgType)>;
using talib_function_t = std::function<void(DataChunk &, void *, idx_t, idx_t, LookbackArgType)>;
};
template <>
struct talib_traits<void> {
using talib_lookback_t = std::function<int()>;
using talib_function_t = std::function<void(DataChunk &, void *, idx_t, idx_t)>;
};
struct X {
talib_traits<int>::talib_lookback_t talib_lookback_int = nullptr;
talib_traits<int>::talib_function_t talib_function_int = nullptr;
talib_traits<double>::talib_lookback_t talib_lookback_double = nullptr;
talib_traits<double>::talib_function_t talib_function_double = nullptr;
talib_traits<void>::talib_lookback_t talib_lookback_void = nullptr;
talib_traits<void>::talib_function_t talib_function_void = nullptr;
explicit X(talib_traits<int>::talib_lookback_t talib_lookback, talib_traits<int>::talib_function_t talib_function)
: talib_lookback_int(talib_lookback), talib_function_int(talib_function) {
}
explicit X(talib_traits<double>::talib_lookback_t talib_lookback,
talib_traits<double>::talib_function_t talib_function)
: talib_lookback_double(talib_lookback), talib_function_double(talib_function) {
}
explicit X(talib_traits<void>::talib_lookback_t talib_lookback, talib_traits<void>::talib_function_t talib_function)
: talib_lookback_void(talib_lookback), talib_function_void(talib_function) {
}
};
int main() {
constexpr bool lookback_is_same =
std::is_same<talib_traits<int>::talib_lookback_t, talib_traits<double>::talib_lookback_t>::value;
constexpr bool function_is_same =
std::is_same<talib_traits<int>::talib_function_t, talib_traits<double>::talib_function_t>::value;
static_assert(!lookback_is_same && !function_is_same);
X x([](void) { return 0; }, [](DataChunk &, void *, idx_t, idx_t) {}); // okay
// ambiguous: more than one instance of constructor "X::X" matches the argument list, int or double?
X y([](int) { return 0; }, [](DataChunk &, void *, idx_t, idx_t, int) {});
}
我怎样才能使它们明确,即防止 std::function
我尝试在构造函数前面加explicit
关键字,但这并不能防止歧义。
我认为你不能阻止一个< code>std::函数
可以通过在调用站点将lambda转换为std::函数来避免这种情况。在这种情况下,您不需要为std::function指定类型参数:
X y(std::function([](int) { return 0; }),
[](DataChunk &, void *, idx_t, idx_t, int) {});
现在,使用std::函数的构造函数
人们希望在将lambda转换为std::f时需要额外的返回类型转换
或者,如果将构造函数转换为模板化工厂函数,则可以显式指定类型(godbolt):
struct X {
...
template <typename T>
static X create(typename talib_traits<T>::talib_lookback_t talib_lookback,
typename talib_traits<T>::talib_function_t talib_function) {
X out;
if constexpr(std::is_same_v<T, int>) {
out.talib_lookback_int = talib_lookback;
out.talib_function_int = talib_function;
} else if constexpr(std::is_same_v<T, double>) {
out.talib_lookback_double = talib_lookback;
out.talib_function_double = talib_function;
} else {
static_assert(std::is_same_v<T, void>);
out.talib_lookback_void = talib_lookback;
out.talib_function_void = talib_function;
}
return out;
}
private:
X(){};
};
int main() {
X x = X::create<void>([](void) { return 0; }, [](DataChunk &, void *, idx_t, idx_t) {}); // okay
X y = X::create<int>([](int){ return 0; }, [](DataChunk &, void *, idx_t, idx_t, int) {});
X z = X::create<double>([](double){ return 0; }, [](DataChunk &, void *, idx_t, idx_t, double) {});
}
让适当的std::function
对象被隐式构造显然是失败的——替代方案(显式构造std::function
对象或工厂函数)在用户端有点冗长(即在构造X
对象时)。如果你更喜欢在那里提供更多的舒适性,你需要在实现上投入更多的精力;一种可能的方法是使构造函数成为接受任意类型的模板,并应用静态类型检查来确定是否传递了正确的参数。这可能看起来如下所示。首先,我们需要一些进一步的特征来确定传递的函数实际上接受哪个参数:
template <typename T>
struct FunctionTraits;
template <typename T>
struct FunctionTraits<std::function<int(T)>>
{
using ParameterType = T;
};
template <typename T>
struct FunctionTraits<std::function<void(DataChunk &, void *, idx_t, idx_t, T)>>
{
using ParameterType = T;
};
template <>
struct FunctionTraits<std::function<int()>>
{
using ParameterType = void;
};
template <>
struct FunctionTraits<std::function<void(DataChunk &, void *, idx_t, idx_t)>>
{
using ParameterType = void;
};
template <typename T>
using ParameterType
= typename FunctionTraits<decltype(std::function(std::declval<T>()))>::ParameterType;
现在我们可以在模板构造函数中使用这些:
struct X
{
// ...
template <typename Lookback, typename Function>
X(Lookback l, Function f)
{
using PType = ParameterType<decltype(l)>;
static_assert(std::is_same_v<PType, ParameterType<decltype(f)>>);
if constexpr(std::is_same_v<PType, int>)
{
talib_lookback_int = l;
talib_function_int = f;
}
else if constexpr(std::is_same_v<PType, double>)
{
talib_lookback_double = l;
talib_function_double = f;
}
else if constexpr(std::is_same_v<PType, void>)
{
talib_lookback_void = l;
talib_function_void = f;
}
}
}
如果传递了不匹配的函数(使用不同的ParamType
或者甚至不匹配所讨论的std::function
类型的签名),代码将失败,否则将通过普通构造函数调用优雅地创建预期的对象,请参阅Godbolt上的演示。
受Aconcagua巧妙回答的启发,您可以使用模板和< code>std::enable_if来禁用不必要的重载:
template<typename F, typename R, typename... Args>
constexpr bool is_functype =
std::is_same_v<decltype(std::function(std::declval<F>())),
std::function<R(Args...)>>;
struct X {
talib_traits<int>::talib_lookback_t talib_lookback_int = nullptr;
talib_traits<int>::talib_function_t talib_function_int = nullptr;
talib_traits<double>::talib_lookback_t talib_lookback_double = nullptr;
talib_traits<double>::talib_function_t talib_function_double = nullptr;
talib_traits<void>::talib_lookback_t talib_lookback_void = nullptr;
talib_traits<void>::talib_function_t talib_function_void = nullptr;
template<typename F, std::enable_if_t<is_functype<F, int, int>, int> = 0>
X(F talib_lookback,
talib_traits<int>::talib_function_t talib_function)
: talib_lookback_int(talib_lookback),
talib_function_int(talib_function)
{}
template<typename F, std::enable_if_t<is_functype<F, int, double>, int> = 0>
X(F talib_lookback,
talib_traits<double>::talib_function_t talib_function)
: talib_lookback_double(talib_lookback),
talib_function_double(talib_function)
{}
};
戈德博尔特