这种情况如何消除几百个 if/else

244 天前
 aqtata

运行时从外部读取一个 16 进制数字,然后调用对应的函数,比如读取到1F3,那么就调用函数foo_1f3,函数参数也是有编号的,规律是这样:

void foo_1f0(myclass_1f0& val);
void foo_1f1(myclass_1f1& val);
void foo_1f2(myclass_1f2& val);

之前 C#是用的反射,很容易实现。到 C++这不知道怎么搞比较优雅,目前有上百个 if/else 去判断然后调用。

C++这边可以用到 C++20 ,不知道有什么酷的解决方法?

9265 次点击
所在节点    C++
63 条回复
lichao
244 天前
std::map
liuguangxuan
244 天前
模板。
blu10ph
244 天前
你是否在找:工厂模式/策略模式?

感觉你描述的还比较清晰,把你发的这段内容交给 AI,一会就能出来代码~
yuzii
244 天前
std::vector<std::function<void(int)>> foos;
aqtata
244 天前
@yuzii 参数不同,而且注册也得写几百行,感觉和 if/else 差不多。
aqtata
244 天前
@lichao 注册函数?感觉和 if/else 差不多,也要写上百行。
aqtata
244 天前
@liuguangxuan 展开讲讲。。
YakumoZi
244 天前
@liuguangxuan 模板是编译时计算,他这个要求运行时读取,应该做不到吧?
zwy100e72
244 天前
对于分发用的标志 `1fx` 比较集中的情况,可以直接用 `std::vector<std::function<>>` 然后用数组下标做 key ;对于 key 比较稀疏的情况,可以用 `std::map<int, std::function<>>` 或者 `std::unordered_map`

注册要写几百行这种估计跑不掉的,目前 c++ 还没有正式支持反射;不过可以用部分编译器支持的 `[[constructor]]` 语法,在启动阶段实现自动注册。例如有某 `Dispatcher::register(int, std::function<>)` 方法,部分编译器可以实现:

```c++
[[gnu::constructor]] reg_handler() { Dispatcher::register(0x1f3, foo_1f3); }
```
fyxtc
244 天前
对于无反射的静态编译型语言,这种情况 if 和 switch 是最清晰的,因为阅读代码的人只要看三个 if 就知道这坨是干什么的,也非常容易定位和修改,用其他自以为“优雅”的解决方案很大概率只是满足了自己苦了他人。
Metatron7
244 天前
不想写 ifelse 就 LLM 生成得了
zwy100e72
244 天前
写了一个超省略的 demo ,供参考 https://godbolt.org/z/ox7K6sM1e
leonshaw
244 天前
直接 switch ,case 用宏包一下,再用工具生成。
强行搞可以把函数导出,运行时查符号。
guanzhangzhang
244 天前
怎么感觉你是在搞啥协议解析,前面是 msgID 对应不同的解析函数,去处理后面的 data 部分
geelaw
244 天前
wow 这个问题完全 under-specified 。第一个问题:你知道要调用什么函数了,可你怎么制造不同类型的参数传入之?

但楼主不应该尝试回答我的第一个问题,而是应该直接说自己实际上要解决的问题,而不是来问自己觉得可行的一半解决方法的另一半。
chashao
244 天前
额,感觉还得用脚本批量生成函数注册的逻辑。。
```c++
void foo_1f0(int& value) { std::cout << "test!!" << std::endl; }

// 这块代码得脚本生成
std::unordered_map<std::string, std::function<void(int&)>> GFunctions = {
{ "foo_1f0", foo_1f0 }
};

int main()
{
int inParam = 0x1f0;

std::stringstream ss;
ss << std::hex<<inParam;
std::string funcName = "foo_" + ss.str();

int param = 1;
GFunctions[funcName](param);
}
```
c3de3f21
244 天前
这样写是最好的
sapphire
244 天前
这种情况适宜从整体项目 build 的角度考量,有工程上的设计和考量。如果语言本身没有反射机制,一般用脚本或其他自定义工具,在 Pre-build 阶段生成函数的桩,同时对参数和调用场景做必要的安全检查。这样对于函数实现代码,只要做好必要的标注,就和 C#没什么区别了。
MoYi123
244 天前
想要酷可以参考 std::visit 的做法. 编译期生成一个 Invoke_array, 下面的例子是从运行期的 int 转特定类型到 lambda 中的例子. 稍微改改就能用于你的需求.

using namespace std;

constexpr std::array cached_type_ints = {1};

struct void_ptr {
int type;
void *ptr;
};

template <int T> struct Int2Type;

template <> struct Int2Type<1> {
using type = int;
};

template <typename Func, typename> class Visitor;

template <typename Func, std::size_t... pos>
class Visitor<Func, std::index_sequence<pos...>> {
public:
using return_type =
std::invoke_result_t<Func, Int2Type<cached_type_ints[0]>::type *>;
using fn_type = return_type (*)(Func &&, void *);

template <int16_t type_int>
static auto func_wrapper(Func &&func, void *ptr) -> return_type {
using real_type = typename Int2Type<type_int>::type;
return func(static_cast<real_type *>(ptr));
}

static auto visit(Func &&func, const void_ptr &item) -> return_type {
constexpr static std::array<fn_type, cached_type_ints.size()> invoke_map = {
func_wrapper<cached_type_ints[pos]>...};

size_t idx = std::ranges::lower_bound(cached_type_ints.begin(),
cached_type_ints.end(), item.type) -
cached_type_ints.begin();
if (idx >= invoke_map.size() or cached_type_ints[idx] != item.type)
[[unlikely]] {
throw std::bad_variant_access();
}
return invoke_map[idx](std::forward<Func>(func), item.ptr);
}
};

template <typename Func> auto visit(Func &&func, const void_ptr &item) {
using visitor = Visitor<decltype(func),
std::make_index_sequence<cached_type_ints.size()>>;
return visitor::visit(std::forward<Func>(func), item);
}

inline auto usage() {
auto item = void_ptr{.ptr = new int(1), .type = 1};
visit(
[](auto *ptr) {
print(*ptr);
delete ptr;
},
item);
}
securityCoding
244 天前
查表 map<key,func>

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://ex.noerr.eu.org/t/1105424

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX