【发布时间】:2018-04-21 14:00:44
【问题描述】:
我有一个遗留的 C 代码库,我正在以零碎的方式迁移到 C++。它包括一个解释器,因此需要包装静态函数和参数以供解释器使用。因此,导出到解释器的典型函数可能具有以下签名:
static void do_strstr(struct value * p)
并像这样暴露给解释器:
using vptr = void (*) ();
template <typename Func>
constexpr vptr to_vptr(Func && func)
{ return reinterpret_cast<vptr>(func); }
struct function string_funs[] = {
...
{ C_FN3, X_A3, "SSI", to_vptr(do_strstr), "find" },
...
};
这已被证明是有效的。到目前为止,该方法的缺点是被调用的函数必须将内存分配到临时堆栈上。例如,一个改进是被调用函数只返回一个字符串。然后包装这个函数,包装器在幕后执行记忆魔法。这允许以更普通的方式创建函数。
这是一个使用我改进的方法连接两个字符串的实现:
static std::string do_concata(struct value* p)
{
std::string s1 = (p)->gString();
std::string s2 = (p+1)->gString();
return s1+s2;
}
我创建了一个辅助函数:
static void do_concata_1(struct value* p)
{
wrapfunc(do_concata)(p);
}
有些通用的包装器定义为:
std::function<void(struct value*)>
wrapfunc(std::function<std::string(struct value*)> func)
{
auto fn = [=](struct value* p) {
std::string s = func(p);
char* ret = alloc_tmp_mem(s.size()+1);
strcpy(ret, s.c_str());
p->sString(ret);
return;
};
return fn;
}
向解释器公开如下:
struct function string_funs[] = {
...
{ C_FN2, X_A2, "SS", to_vptr(do_concata_1), "concata" },
...
};
不过,我对这个解决方案并不满意,因为我定义的每个函数都需要一个辅助函数。如果我能消除do_concata_1 并编写另一个包装wrapfunc 的函数会更好。
这就是问题所在。如果我写:
vptr to_vptr_1(std::function<void(struct value*)> func)
{
return to_vptr(wrapfunc(func));
}
然后编译器抱怨:
stringo.cc: In function ‘void (* to_vptr_1(std::function<void(value*)>))()’:
stringo.cc:373:30: error: could not convert ‘func’ from ‘std::function<void(value*)>’ to ‘std::function<std::__cxx11::basic_string<char>(value*)>’
return to_vptr(wrapfunc(func));
这在我看来很奇怪,因为std::__cxx11::basic_string<char> 是从哪里来的?应该是void,确定吗?
我不知道应该修复什么。对于我应该传递函数的副本、函数的引用还是神秘的 && r-vale 引用,我也有点困惑。
【问题讨论】:
-
这里唯一现实的解决办法是用火烧掉整个东西,然后使用现代 C++ 以类型安全的形式从头开始重新实现它。
-
@Sam。代码库很繁琐,这是真的,但我宁愿重构也不愿重写。还要记住,因为函数是在解释器中使用的,所以无论如何都必须有一种机制来包装和解包函数。整个 alloc_tmp_mem() 和 strcpy() 是不幸的,但不是灾难。至少在我提议的重构中,在代码中创建了一个关键点。这将允许我在以后的某个时间点改进代码。
标签: c++