rid*_*ish 91 c++ portability const
在我的Linux(和OS X)机器上,该iconv()
函数具有以下原型:
size_t iconv (iconv_t, char **inbuf...
Run Code Online (Sandbox Code Playgroud)
而在FreeBSD上它看起来像这样:
size_t iconv (iconv_t, const char **inbuf...
Run Code Online (Sandbox Code Playgroud)
我希望我的C++代码能够在两个平台上构建.C编译器,传递char**
用于一个const char**
参数(或反之亦然)通常发射一个单纯的警告; 但是在C++中,这是一个致命的错误.因此,如果我传递一个char**
,它将无法在BSD上编译,如果我传递const char**
它将无法在Linux/OS X上编译.如何编写可编译在两者上的代码,而无需尝试检测平台?
我有一个(失败的)想法是提供一个覆盖标题提供的任何本地原型:
void myfunc(void) {
size_t iconv (iconv_t, char **inbuf);
iconv(foo, ptr);
}
Run Code Online (Sandbox Code Playgroud)
这个失败是因为iconv
需要C链接,你不能放在extern "C"
一个函数内(为什么不呢?)
我想出的最好的工作思路是抛出函数指针本身:
typedef void (*func_t)(iconv_t, const char **);
((func_t)(iconv))(foo, ptr);
Run Code Online (Sandbox Code Playgroud)
但这有可能掩盖其他更严重的错误.
Nor*_*ame 57
如果您想要的只是对某些const问题视而不见,那么您可以使用模糊区别的转换,即使char**和const char**可互操作:
template<class T>
class sloppy {};
// convert between T** and const T**
template<class T>
class sloppy<T**>
{
T** t;
public:
sloppy(T** mt) : t(mt) {}
sloppy(const T** mt) : t(const_cast<T**>(mt)) {}
operator T** () const { return t; }
operator const T** () const { return const_cast<const T**>(t); }
};
Run Code Online (Sandbox Code Playgroud)
然后在程序中:
iconv(c, sloppy<char**>(&in) ,&inlen, &out,&outlen);
Run Code Online (Sandbox Code Playgroud)
sloppy()接受a char**
或a const char*
并将其转换为a char**
或a const char*
,无论iconv要求的第二个参数如何.
更新:更改为使用const_cast并调用sloppy而不是as cast.
Jam*_*lis 33
您可以通过检查声明的函数的签名来消除两个声明之间的歧义.以下是检查参数类型所需模板的基本示例.这可以很容易地推广(或者你可以使用Boost的函数特性),但这足以证明你的特定问题的解决方案:
#include <iostream>
#include <stddef.h>
#include <type_traits>
// I've declared this just so the example is portable:
struct iconv_t { };
// use_const<decltype(&iconv)>::value will be 'true' if the function is
// declared as taking a char const**, otherwise ::value will be false.
template <typename>
struct use_const;
template <>
struct use_const<size_t(*)(iconv_t, char**, size_t*, char**, size_t*)>
{
enum { value = false };
};
template <>
struct use_const<size_t(*)(iconv_t, char const**, size_t*, char**, size_t*)>
{
enum { value = true };
};
Run Code Online (Sandbox Code Playgroud)
这是一个演示行为的示例:
size_t iconv(iconv_t, char**, size_t*, char**, size_t*);
size_t iconv_const(iconv_t, char const**, size_t*, char**, size_t*);
int main()
{
using std::cout;
using std::endl;
cout << "iconv: " << use_const<decltype(&iconv) >::value << endl;
cout << "iconv_const: " << use_const<decltype(&iconv_const)>::value << endl;
}
Run Code Online (Sandbox Code Playgroud)
一旦可以检测到参数类型的限定,就可以编写两个调用的包装函数iconv
:一个iconv
用char const**
参数调用iconv
,另一个用char**
参数调用.
因为应该避免功能模板专业化,所以我们使用类模板来进行专业化.请注意,我们还使每个调用者都成为一个函数模板,以确保只实例化我们使用的特化.如果编译器尝试为错误的特化生成代码,则会出现错误.
然后我们用a call_iconv
来包装这些用法,使调用就像iconv
直接调用一样简单.以下是显示如何编写的一般模式:
template <bool UseConst>
struct iconv_invoker
{
template <typename T>
static size_t invoke(T const&, /* arguments */) { /* etc. */ }
};
template <>
struct iconv_invoker<true>
{
template <typename T>
static size_t invoke(T const&, /* arguments */) { /* etc. */ }
};
size_t call_iconv(/* arguments */)
{
return iconv_invoker<
use_const<decltype(&iconv)>::value
>::invoke(&iconv, /* arguments */);
}
Run Code Online (Sandbox Code Playgroud)
(后一种逻辑可以被清理和推广;我已经尝试将每一部分都明确化,以便更清楚地了解它是如何工作的.)
Kri*_*izz 11
您可以使用以下内容:
template <typename T>
size_t iconv (iconv_t i, const T inbuf)
{
return iconv(i, const_cast<T>(inbuf));
}
void myfunc(void) {
const char** ptr = // ...
iconv(foo, ptr);
}
Run Code Online (Sandbox Code Playgroud)
您可以通过const char**
Linux/OSX它将通过模板功能,在FreeBSD上它将直接进入iconv
.
缺点:它将允许调用iconv(foo, 2.5)
将使编译器无限重复.
#ifdef __linux__
... // linux code goes here.
#elif __FreeBSD__
... // FreeBSD code goes here.
#endif
Run Code Online (Sandbox Code Playgroud)
这里有所有操作系统的ID.对我来说,没有任何意义,在没有检查这个系统的情况下尝试做依赖于操作系统的事情.这就像买绿色裤子但不看它们.
归档时间: |
|
查看次数: |
5369 次 |
最近记录: |