我正在嘗試為我的庫的用戶提供他們自己的計算函式,這些函式需要維護規則:
- 回傳一個
float值 - 可能需要 0 到 7 個
float引數 - 不可能有例外
我有一個解決方案 - 見下文 - 但它笨重且丑陋。我敢打賭,有一種更優雅的方法可以使用模板和 type_traits 來解決它,但我無法理解它。
// Copyright 2022 bla
#include <cstdint>
#include <iostream>
#include <functional>
#include <math.h>
using std::cout;
using std::endl;
class D {
public:
using Dlambda0 = std::function<float()>;
using Dlambda1 = std::function<float(float)>;
using Dlambda2 = std::function<float(float, float)>;
using Dlambda3 = std::function<float(float, float, float)>;
using Dlambda4 = std::function<float(float, float, float, float)>;
using Dlambda5 = std::function<float(float, float, float, float, float)>;
using Dlambda6 = std::function<float(float, float, float, float, float, float)>;
using Dlambda7 = std::function<float(float, float, float, float, float, float, float)>;
D(const char *name, Dlambda0 dFn) : n(name), fn0 {std::move(dFn)} { cout << name << ":" << __PRETTY_FUNCTION__ << endl; arity = 0; }
D(const char *name, Dlambda1 dFn) : n(name), fn1 {std::move(dFn)} { cout << name << ":" << __PRETTY_FUNCTION__ << endl; arity = 1; }
D(const char *name, Dlambda2 dFn) : n(name), fn2 {std::move(dFn)} { cout << name << ":" << __PRETTY_FUNCTION__ << endl; arity = 2; }
D(const char *name, Dlambda3 dFn) : n(name), fn3 {std::move(dFn)} { cout << name << ":" << __PRETTY_FUNCTION__ << endl; arity = 3; }
D(const char *name, Dlambda4 dFn) : n(name), fn4 {std::move(dFn)} { cout << name << ":" << __PRETTY_FUNCTION__ << endl; arity = 4; }
D(const char *name, Dlambda5 dFn) : n(name), fn5 {std::move(dFn)} { cout << name << ":" << __PRETTY_FUNCTION__ << endl; arity = 5; }
D(const char *name, Dlambda6 dFn) : n(name), fn6 {std::move(dFn)} { cout << name << ":" << __PRETTY_FUNCTION__ << endl; arity = 6; }
D(const char *name, Dlambda7 dFn) : n(name), fn7 {std::move(dFn)} { cout << name << ":" << __PRETTY_FUNCTION__ << endl; arity = 7; }
float operator() () { return callForArity(0, nanf(""), nanf(""), nanf(""), nanf(""), nanf(""), nanf(""), nanf("")); }
float operator() (float a) { return callForArity(1, a, nanf(""), nanf(""), nanf(""), nanf(""), nanf(""), nanf("")); }
float operator() (float a, float b) { return callForArity(2, a, b, nanf(""), nanf(""), nanf(""), nanf(""), nanf("")); }
float operator() (float a, float b, float c) { return callForArity(3, a, b, c, nanf(""), nanf(""), nanf(""), nanf("")); }
float operator() (float a, float b, float c, float d) { return callForArity(4, a, b, c, d, nanf(""), nanf(""), nanf("")); }
float operator() (float a, float b, float c, float d, float e) { return callForArity(5, a, b, c, d, e, nanf(""), nanf("")); }
float operator() (float a, float b, float c, float d, float e, float f) { return callForArity(6, a, b, c, d, e, f, nanf("")); }
float operator() (float a, float b, float c, float d, float e, float f, float g) { return callForArity(7, a, b, c, d, e, f, g); }
protected:
const char *n;
Dlambda0 fn0;
Dlambda1 fn1;
Dlambda2 fn2;
Dlambda3 fn3;
Dlambda4 fn4;
Dlambda5 fn5;
Dlambda6 fn6;
Dlambda7 fn7;
uint8_t arity;
float callForArity(uint8_t givenArgs, float a, float b, float c, float d, float e, float f, float g) {
switch (arity) {
case 0:
return fn0();
break;
case 1:
return fn1(a);
break;
case 2:
return fn2(a, b);
break;
case 3:
return fn3(a, b, c);
break;
case 4:
return fn4(a, b, c, d);
break;
case 5:
return fn5(a, b, c, d, e);
break;
case 6:
return fn6(a, b, c, d, e, f);
break;
case 7:
return fn7(a, b, c, d, e, f, g);
break;
}
return nanf("");
}
};
float fix1(float a) {
return a * a;
}
int main(int argc, char **argv) {
// register some functions
D function("bla", { []() { return 5; }});
D function2("blubb", { [](float x, float y) { return x y; }});
D function3("Blort", fix1);
cout << function() << endl; // Okay
cout << function2(1.0, 5.5) << endl; // Okay
cout << function2(5.5) << endl; // Aw!
cout << function3(5.5) << endl; // okay
return 0;
}
uj5u.com熱心網友回復:
sizeof...您對and有完全正確的想法static_assert。我們可以D獲取一個引數包并從中構建函式型別。
template <typename... Ts>
class D {
public:
using function_type = std::function<float(Ts...)>;
...
};
很簡單。現在斷言。這些只是進入類體。聽起來你基本上已經弄清楚了這個sizeof...部分。
static_assert(sizeof...(Ts) <= 7, "Too many arguments");
現在,對于float,聽起來您希望所有的Ts都是字面意思 float(即is_same)。如果“可轉換為float”對于您的用例來說足夠強大,您可以考慮在這些示例中替換is_same為。無論如何,您標記了 C 11,但如果我們有 C 17,我們可以使用fold 運算式is_convertible非常輕松地撰寫此斷言。
static_assert((std::is_same<Ts, float>::value && ...), "All args must be floats");
如果您沒有 C 17,我們可以使用@Columbo 的絕妙all_true技巧。
template <bool...>
struct bool_pack;
template <bool... v>
using all_true = std::is_same<bool_pack<true, v...>, bool_pack<v..., true>>;
static_assert(all_true<std::is_same<Ts, float>::value...>::value, "All args must be floats");
現在我們可以通過指定它們采用的浮點數來建構式
D<> function("bla", []() { return 5; });
D<float, float> function2("blubb", [](float x, float y) { return x y; });
D<float> function3("Blort", fix1);
我們可以打電話給他們
std::cout << function() << std::endl;
std::cout << function2(1.0, 5.5) << std::endl;
std::cout << function3(5.5) << std::endl;
如果我們嘗試function2(5.5),我們會得到.... 不錯的錯誤訊息,根據 C 模板標準
so_vv.cpp: In function ‘int main()’:
so_vv.cpp:47:29: error: no match for call to ‘(D<float, float>) (double)’
47 | std::cout << function2(5.5) << std::endl; // Aw!
| ^
so_vv.cpp:30:9: note: candidate: ‘float D<Ts>::operator()(Ts ...) [with Ts = {float, float}]’
30 | float operator()(Ts... args) {
| ^~~~~~~~
so_vv.cpp:30:9: note: candidate expects 2 arguments, 1 provided
我們可以使我們的其他斷言失敗。
D<int> not_allowed("foobar", [](int x) { return 10; });
D<float, float, float, float, float, float, float, float, float> not_allowed2("foobar", [](...) { return 10; });
static_assert這些將在編譯時使我們的呼叫失敗。
如果你不喜歡顯式的D<float, float, float>模板引數,你絕對可以D<3>通過巧妙地使用std::integer_sequence. 至于您是否可以讓 C 完全推斷引數計數,我不確定。我沒有運氣讓型別推斷走那么遠,但你可能會有更好的運氣。
在線嘗試!
轉載請註明出處,本文鏈接:https://www.uj5u.com/qiye/532044.html
上一篇:添加到整數串列
下一篇:通用功能問題
