91

私の Linux (および OS X) マシンでは、iconv()関数には次のプロトタイプがあります。

size_t iconv (iconv_t, char **inbuf...

一方、FreeBSD では次のようになります。

size_t iconv (iconv_t, const char **inbuf...

私の C++ コードを両方のプラットフォームでビルドしたいと考えています。C コンパイラでchar**は、const char**パラメータに a を渡す (またはその逆) と、通常は単なる警告が出力されます。ただし、C++ では致命的なエラーです。したがって、 a を渡すchar**と BSD でコンパイルされず、 a を渡すとconst char**Linux / OS X でコンパイルされません。プラットフォームを検出しようとせずに、両方でコンパイルされるコードを作成するにはどうすればよいでしょうか?

私が持っていた (失敗した) アイデアの 1 つは、ヘッダーによって提供されるものをオーバーライドするローカル プロトタイプを提供することでした。

void myfunc(void) {
    size_t iconv (iconv_t, char **inbuf);
    iconv(foo, ptr);
}

iconvCリンケージが必要であり、関数内に配置できないため、これは失敗しextern "C"ます(なぜですか?)

私が思いついた最も効果的なアイデアは、関数ポインター自体をキャストすることです。

typedef void (*func_t)(iconv_t, const char **);
((func_t)(iconv))(foo, ptr);

しかし、これは他のより深刻なエラーを隠す可能性があります。

4

9 に答える 9

56

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

その後、プログラムの後半で:

iconv(c, sloppy<char**>(&in) ,&inlen, &out,&outlen);

sloppy() は a または a を取り、char**iconvの 2 番目のパラメーターが要求するものは何でも、それを aまたは aconst char*に変換します。char**const char*

更新: const_cast を使用し、as キャストではなく sloppy を呼び出すように変更されました。

于 2012-07-10T21:45:51.920 に答える
33

宣言された関数のシグネチャを調べることで、2 つの宣言を明確にすることができます。パラメータの型を検査するために必要なテンプレートの基本的な例を次に示します。これは簡単に一般化できます (または 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 };
};

動作を示す例を次に示します。

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

パラメーターの型の修飾を検出できたら、 を呼び出す 2 つのラッパー関数をiconv作成iconvできます。char const**iconvchar**

関数テンプレートの特殊化は避ける必要があるため、クラス テンプレートを使用して特殊化を行います。また、使用する特殊化のみがインスタンス化されるように、各呼び出し元を関数テンプレートにすることに注意してください。コンパイラが間違った特殊化のコードを生成しようとすると、エラーが発生します。

次に、これらの使用法を 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 */);
}

(この後者のロジックはクリーンアップして一般化することができます。うまくいけば、それがどのように機能するかを明確にするために、その各部分を明示的にしようとしました。)

于 2012-07-10T20:52:59.090 に答える
7
#ifdef __linux__
... // linux code goes here.
#elif __FreeBSD__
... // FreeBSD code goes here.
#endif

ここには、すべてのオペレーティング システムの ID があります。私にとって、このシステムをチェックせずに、オペレーティング システムに依存する何かをしようとする意味はありません。緑のズボンを買うのに見ずに買うようなものです。

于 2012-07-10T20:36:28.230 に答える
1

どうですか:

#include <cstddef>
using std::size_t;

// test harness, these definitions aren't part of the solution
#ifdef CONST_ICONV
    // other parameters removed for tediousness
    size_t iconv(const char **inbuf) { return 0; }
#else
    // other parameters removed for tediousness
    size_t iconv(char **inbuf) { return 0; }
#endif

// solution
template <typename T>
size_t myconv_helper(size_t (*system_iconv)(T **), char **inbuf) {
    return system_iconv((T**)inbuf); // sledgehammer cast
}

size_t myconv(char **inbuf) {
    return myconv_helper(iconv, inbuf);
}

// usage
int main() {
    char *foo = 0;
    myconv(&foo);
}

これはC++03の厳密なエイリアシングに違反していると思いますが、C ++ 11ではいわゆる「類似型」const char**であるため、C++11では違反していません。を作成し、それをに設定し、一時へのポインタを使用して呼び出し、結果をコピーして:の後に戻すchar**以外に、厳密なエイリアシングの違反を回避することはできません。const char**fooiconv*fooconst_cast

template <typename T>
size_t myconv_helper(size_t (*system_iconv)(T **), char **inbuf) {
    T *tmpbuf;
    tmpbuf = *inbuf;
    size_t result = system_iconv(&tmpbuf);
    *inbuf = const_cast<char*>(tmpbuf);
    return result;
}

これは、const-correctnessのPOVから安全です。これiconvinbuf、そこに格納されているポインターをインクリメントするだけだからです。したがって、最初に見たときに非constであったポインターから派生したポインターから「constをキャストアウェイ」しています。

また、のオーバーロードを記述して、他の方向に物事を取り込んで混乱させることもできますmyconv。これにより、呼び出し元は、を渡すか、を渡すかを選択myconv_helperできます。これは間違いなくC++で最初に呼び出し元に与えられるべきでしたが、もちろん、インターフェイスは関数のオーバーロードがないCからコピーされただけです。const char **inbufconst char**char**iconv

于 2012-07-10T22:18:16.570 に答える
1

更新: autotools を使用せずに C++ で処理できることがわかりましたが、探している人のために autoconf ソリューションを残しています。

あなたが探しているのはiconv.m4、gettext パッケージによってインストールされるものです。

AFAICSそれはただです:

AM_ICONV

configure.ac で、正しいプロトタイプを検出する必要があります。

次に、使用するコードで:

#ifdef ICONV_CONST
// const char**
#else
// char**
#endif
于 2012-07-10T20:53:22.367 に答える
1

どうですか

static void Test(char **)
{
}

int main(void)
{
    const char *t="foo";
    Test(const_cast<char**>(&t));
    return 0;
}

編集:もちろん、「プラットフォームを検出せずに」は少し問題です。おっとっと :-(

EDIT 2: OK、改良版かな?

static void Test(char **)
{
}

struct Foo
{
    const char **t;

    operator char**() { return const_cast<char**>(t); }
    operator const char**() { return t; }

    Foo(const char* s) : t(&s) { }
};

int main(void)
{
    Test(Foo("foo"));
    return 0;
}
于 2012-07-10T20:41:12.530 に答える
0

私はこのパーティーに遅れていますが、それでも私の解決策は次のとおりです。

// This is here because some compilers (Sun CC) think that there is a
// difference if the typedefs are not in an extern "C" block.
extern "C"
{
//! SUSv3 iconv() type.
typedef size_t (& iconv_func_type_1) (iconv_t cd, char * * inbuf,
    size_t * inbytesleft, char * * outbuf, size_t * outbytesleft); 


//! GNU iconv() type.
typedef size_t (& iconv_func_type_2) (iconv_t cd, const char * * inbuf,
    size_t * inbytesleft, char * * outbuf, size_t * outbytesleft);
} // extern "C"

//...

size_t
call_iconv (iconv_func_type_1 iconv_func, char * * inbuf,
    size_t * inbytesleft, char * * outbuf, size_t * outbytesleft)
{
    return iconv_func (handle, inbuf, inbytesleft, outbuf, outbytesleft);
}

size_t
call_iconv (iconv_func_type_2 iconv_func, char * * inbuf,
    size_t * inbytesleft, char * * outbuf, size_t * outbytesleft)
{
    return iconv_func (handle, const_cast<const char * *>(inbuf),
        inbytesleft, outbuf, outbytesleft);
}

size_t
do_iconv (char * * inbuf, size_t * inbytesleft, char * * outbuf,
    size_t * outbytesleft)
{
    return call_iconv (iconv, inbuf, inbytesleft, outbuf, outbytesleft);
}
于 2012-07-21T21:35:44.923 に答える