2012-07-10 4 views
91

iconv()関数は、このプロトタイプを持っている:どのプラットフォームでchar **を使用し、他のプラットフォームでconst char **を移植するC++関数を移植可能に呼び出すことができますか?私のLinux(およびOS X)マシンで

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

FreeBSD上で、それは次のようになりながら:

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

私が構築するために私のC++のコードをしたいと思います両方のプラットフォーム。 Cコンパイラでは、const char**パラメータにchar**(またはその逆)を渡すと、通常は警告が出されます。 C++では致命的なエラーです。だから私がchar**を渡すと、BSDでコンパイルされません。const char**を渡すと、Linux/OS Xでコンパイルされません。どのようにしてプラットフォームを検出しようとせずに、両方でコンパイルするコードを書くことができますか?私が持っていた(失敗した)

一つ

アイデアは、任意のヘッダが提供するよりも優先されますローカルプロトタイプを提供することでした:

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

iconvはCリンケージを必要とするので、これは失敗し、あなたが関数内extern "C"を置くことはできません(理由?ない)

私が思い付くした最高のワーキングアイデアは関数ポインタ自体をキャストすることです:

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

が、T彼は他の重大な誤りを隠す可能性があります。

+0

か? – nims

+31

あなたの最初のための質問の地獄。 :) – Almo

+24

FreeBSDにバグを記録する。 POSIXの['iconv'](http://pubs.opengroup.org/onlinepubs/009695399/functions/iconv.html)の実装では、' inbuf'は非constでなければなりません。 – dreamlax

答えて

57

あなたが望むのは、いくつかのconst問題を盲目的に見ているだけであれば、その区別をぼかす変換を使用できます。そして

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

)、char**又はconst char*をとり、char**またはconst char*に変換する相互運用**チャーとのconst charが作りますiconvの第2引数が何であれ

UPDATE:const_castを使用するように変更されました。

+0

これはかなりうまく動作し、C++ 11を必要とせずに安全で簡単なようです。私はそれと一緒に行くよ!ありがとう! –

+2

私の答えで言ったように、これはC++ 03の厳密なエイリアシングに違反していると思うので、その意味でC++ 11が必要です。誰もがこれを守りたいと思うなら、私は間違っているかもしれません。 –

+1

C++でCスタイルのキャストを推奨しないでください。私が間違っていない限り、あなたは 'sloppy ()'イニシャライザをそこに直接呼び出すことができます。 –

7
#ifdef __linux__ 
... // linux code goes here. 
#elif __FreeBSD__ 
... // FreeBSD code goes here. 
#endif 

HereすべてのオペレーティングシステムのIDがあります。私にとっては、このシステムをチェックせずにオペレーティングシステムに依存して何かをしようとすると、何のポイントもありません。それは緑色のズボンを買うようなものだが、彼らを見ることはない。

+13

しかし、質問者は明示的に「プラットフォームを検出しようとせずに」と言っています。 –

+0

@FrédéricHamidi:プラットフォームをチェックするのが最も信頼できる方法です。 – Linuxios

+1

@Linuxios:LinuxベンダーやAppleが[POSIX標準](http://pubs.opengroup.org/onlinepubs/7908799/xsh/iconv.html)に従いたいと決めるまで。この種のコーディングは、維持するのが難しいことが知られています。 –

1

どの程度

static void Test(char **) 
{ 
} 

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

EDIT:もちろん、「プラットフォームを検出することなく」は、問題のビットです。 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; 
} 
+0

これは、他のプラットフォームではコンパイルできないという問題があります(つまり、関数が 'const char **'を取ると失敗します)。 –

1

独自のラッパー関数を使用することが許容されていることを示してきたあなたはまた、警告と一緒に暮らすことをいとわないように見える

ので、代わりにC++であなたのラッパーを書くの、あなただけのいくつかのシステム上の警告を取得しますC、でそれを書く:

// my_iconv.h 

#if __cpluscplus 
extern "C" { 
#endif 

size_t my_iconv(iconv_t cd, char **restrict inbuf, ?* etc... */); 


#if __cpluscplus 
} 
#endif 



// my_iconv.c 
#include <iconv.h> 
#include "my_iconv.h" 

size_t my_iconv(iconv_t cd, char **inbuf, ?* etc... */) 
{ 
    return iconv(cd, 
       inbuf /* will generate a warning on FreeBSD */, 
       /* etc... */ 
       ); 
} 
34

あなたは2つの宣言の間で明確にすることができます宣言された関数のシグネチャを検査することによって行われます。ここでは、パラメータタイプを検査するために必要なテンプレートの基本的な例を示します。これは、簡単に一般化することができ(またはあなたがブーストの機能の特徴を使用することができます)が、これはあなたの特定の問題の解決策を発揮するのに十分である:

#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を引数char const**で、iconvchar**引数で呼び出すものです。

機能テンプレートの特殊化を避ける必要があるため、クラステンプレートを使用して特殊化を行います。また、invokerのそれぞれを関数テンプレートにして、使用する特殊化のみがインスタンス化されるようにします。コンパイラーが誤った特殊化のコードを生成しようとすると、エラーが発生します。

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

(この後者のロジックをクリーンアップし、一般化することができ、次はこれを書くことができるかを示す一般的なパターンです。)

+3

そこには素晴らしいマジックがあります。 :)私はそれが質問に答えるように見えるので、私はupvoteだろうが、私はそれが動作することを確認していない、と私は十分なハードコアC + +だけでそれを見て知っているか分からない。 :) – Almo

+7

注: 'decltype'にはC++ 11が必要です。 –

+4

注目すべき! C++は離れ、C++はもう一度返す。 –

1

私はautotoolsなしでC++で処理することができますが、探している人のためにautoconfソリューションを残しています。

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

AFAICS:

AM_ICONV 
configure.acで

、それが正しいプロトタイプを検出する必要があります。

次に、あなたが使用しているコードで:

#ifdef ICONV_CONST 
// const char** 
#else 
// char** 
#endif 
+0

はそのためのテンプレート特化を使用します。上記を参照。 – Alex

+1

ありがとう!私はすでにautotoolsを使用していますが、これは問題を回避するための標準的な方法のようですので、完璧でなければなりません!残念ながら、私はiconv.m4ファイルを見つけるためにautoconfを取得できませんでした(古いバージョンのautotoolsを持っているOS Xには存在しないようです)ので、移植可能ではありませんでした。周りには、多くの人がこのマクロで問題を抱えていることがわかります。ああ、オートツール! –

+0

私は答えが醜いが危険ではないハックを持っていると思う。それでも、もしあなたがすでにautoconfを使用していて、必要な設定が気になるプラットフォームにあれば、これを使わない本当の理由はありません。 –

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

あなたはconst char**を渡すことができますおよびLinux/OSX上で、それはテンプレート関数 を経るとなりますFreeBSD上ではiconvに直接行きます。

欠点:それはiconv(foo, 2.5)のような呼び出しを許可し、コンパイラを無限に繰り返します。

+2

ニース!私は、このソリューションには潜在的な可能性があると思います。関数が完全に一致しない場合にのみ、テンプレートを選択するためにオーバーロード解像度を使用するのが好きです。しかし、 'const_cast'は、 'T **'が' const'であるかどうかを検出し、適切な資格を追加または削除する 'T **'を掘り起こす 'add_or_remove_const'に移動する必要があります。これは私が実証した解決策よりもまだ(遠く)より簡単です。少しの作業で、このソリューションを 'const_cast'なしで(つまり' iconv'でローカル変数を使って)動作させることも可能です。 –

+0

素晴らしい作品です。 +1、ありがとう! –

+0

私は何かを忘れましたか?実際の 'iconv'が非constの場合、' T'は 'const char ** 'として推論されません。これは、' inbuf'パラメータが 'const T'型であることを意味します。これは' const char * * const'を呼び出すと、テンプレート内の 'iconv'の呼び出し自体が呼び出されます。ジェームズは言うが、タイプ「T」を適切に修正すると、このトリックが機能する何かの基礎となる。 –

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であるため、C++で11 const char**char**は、いわゆる "類似タイプ" はありません。あなたは、const char*を作成するよりも、他の厳格なエイリアシングの違反を避ける*fooにそれが等しく設定、一時的にポインタでiconvを呼び出し、その後、const_cast後に戻っ*fooに結果をコピーするつもりはありません。

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

iconvのすべてがinbufであるため、これはconst-correctnessのPOVから安全です。だから私たちは最初にそれを見たときに非constだったポインタから派生したポインタから "constをキャスト"しています。

呼び出し側がconst char**またはchar**に合格するかどうかの選択肢を持っているように、我々はまた、const char **inbufを取り、他の方向に約物事を台無しmyconvmyconv_helperの過負荷を書くことができます。おそらくiconvがC++の最初の場所で呼び出し元に与えられるはずですが、もちろんインターフェイスは関数のオーバーロードがないCからコピーされます。

+0

"スーパーpedantry"コードは不要です。現在のstdlibC++を使用しているGCC4.7では、これをコンパイルする必要があります。 –

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); 
} 
関連する問題