这种情况如何消除几百个 if/else
运行时从外部读取一个 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
template <> struct Int2Type<1> {
using type = int;
};
template
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
using visitor = Visitor<decltype(func),
std::make_index_sequence<cached_type_ints.size()>>;
return visitor::visit(std::forward
}
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 嵌套很多层,去消除更有意义一点。
真的烦国内这些 app 的广告,有没有用海外安卓机的老哥说说,如果我换一个平常用有没有什么不方便的地方? google play 该有的 app 都有么,有没有广告? 你一直…
选择一个正确的名字是编程中最重要的事。以前酷壳向大家推荐过两篇文章《编程命名中的7+1个提示》 和《编程中的命名设计那点事》,今天再向大家推荐一篇。一个正确的命名可以让你更容易…
这两款哪个好一点? 还要不要等 OPPO 了? find x8 mini 更好一些 投票 vivo ,系统更加优秀 等一加 mini vivo +1 OPP…