运行时从外部读取一个 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 ,不知道有什么酷的解决方法?

std::map

模板。

你是否在找:工厂模式/策略模式?

感觉你描述的还比较清晰,把你发的这段内容交给 AI,一会就能出来代码~

std::vector<std::function<void(int)>> foos;

参数不同,而且注册也得写几百行,感觉和 if/else 差不多。

注册函数?感觉和 if/else 差不多,也要写上百行。

展开讲讲。。

模板是编译时计算,他这个要求运行时读取,应该做不到吧?

对于分发用的标志 1fx 比较集中的情况,可以直接用 std::vector<std::function<>> 然后用数组下标做 key ;对于 key 比较稀疏的情况,可以用 std::map<int, std::function<>> 或者 std::unordered_map

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

[[gnu::constructor]] reg_handler() { Dispatcher::register(0x1f3, foo_1f3); }

对于无反射的静态编译型语言,这种情况 if 和 switch 是最清晰的,因为阅读代码的人只要看三个 if 就知道这坨是干什么的,也非常容易定位和修改,用其他自以为“优雅”的解决方案很大概率只是满足了自己苦了他人。

不想写 ifelse 就 LLM 生成得了

写了一个超省略的 demo ,供参考 godbolt.org/z/ox7K6sM1e

直接 switch ,case 用宏包一下,再用工具生成。
强行搞可以把函数导出,运行时查符号。

怎么感觉你是在搞啥协议解析,前面是 msgID 对应不同的解析函数,去处理后面的 data 部分

wow 这个问题完全 under-specified 。第一个问题:你知道要调用什么函数了,可你怎么制造不同类型的参数传入之?

但楼主不应该尝试回答我的第一个问题,而是应该直接说自己实际上要解决的问题,而不是来问自己觉得可行的一半解决方法的另一半。

额,感觉还得用脚本批量生成函数注册的逻辑。。

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);
}

这样写是最好的

这种情况适宜从整体项目 build 的角度考量,有工程上的设计和考量。如果语言本身没有反射机制,一般用脚本或其他自定义工具,在 Pre-build 阶段生成函数的桩,同时对参数和调用场景做必要的安全检查。这样对于函数实现代码,只要做好必要的标注,就和 C#没什么区别了。

想要酷可以参考 std::visit 的做法. 编译期生成一个 Invoke_array, 下面的例子是从运行期的 int 转特定类型到 lambda 中的例子. 稍微改改就能用于你的需求.

using namespace std;

constexpr std::array cached_type_ints = {1};

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

template struct Int2Type;

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

template class Visitor;

template
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_mapidx, item.ptr);
}
};

template 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), item);
}

inline auto usage() {
auto item = void_ptr{.ptr = new int(1), .type = 1};
visit(
[](auto *ptr) {
print(*ptr);
delete ptr;
},
item);
}

查表 map<key,func>

宏定义

这种需求本来难以简化的,Map<key, func>已经是不错的选择了。

再想继续想办法简化我觉得只能增加后期维护负担。

符号得和得和字符串对上,简单点就是 dlsym ,复杂一点用 linker set ,自己加 map 也可以,就是没那么优雅了。

请问 您是找 “宏” 的使用方法么

反射有没有?

只是平级的 if else 没必要专门消除吧,看起来也是很清晰的。
你要 if else 嵌套很多层,去消除更有意义一点。