web-dev-qa-db-ja.com

文字列を作成するためのCマクロ

代替タイトル(検索を支援するため)

  • プリプロセッサトークンを文字列に変換します
  • [〜#〜] c [〜#〜]マクロの値からchar文字列を作成する方法は?

元の質問

使用したい[〜#〜] c [〜#〜]#defineコンパイル時にリテラル文字列を作成します。

文字列は、デバッグ、リリースなどのために変更されるドメインです。

私はこのようないくつかのことをしたいと思います:

#ifdef __TESTING
    #define IV_DOMAIN domain.org            //in house testing
#Elif __LIVE_TESTING
    #define IV_DOMAIN test.domain.com       //live testing servers
#else
    #define IV_DOMAIN domain.com            //production
#endif

// Sub-Domain
#define IV_SECURE "secure.IV_DOMAIN"             //secure.domain.org etc
#define IV_MOBILE "m.IV_DOMAIN"

しかし、プリプロセッサは「」内の何も評価しません

  1. これを回避する方法はありますか?
  2. これもいい考えですか?
30
rjstelling

Cでは、文字列リテラルは自動的に連結されます。例えば、

const char * s1 = "foo" "bar";
const char * s2 = "foobar";

s1およびs2は同じ文字列です。

だから、あなたの問題については、答えは(トークンの貼り付けなしで)

#ifdef __TESTING
    #define IV_DOMAIN "domain.org"
#Elif __LIVE_TESTING
    #define IV_DOMAIN "test.domain.com"
#else
    #define IV_DOMAIN "domain.com"
#endif

#define IV_SECURE "secure." IV_DOMAIN
#define IV_MOBILE "m." IV_DOMAIN
38
Alex B

これを行うには、いくつかの方法があります。

  1. 文字列リテラルのみを扱っている場合は、単に文字列を使用するだけで済みます。文字列リテラルを次々に配置すると、コンパイラはそれらを連結します。

  2. 文字列リテラル以外のものが関係している可能性がある場合(つまり、マクロから新しい識別子を作成している場合)は、「##」プリプロセッサトークン貼り付け演算子を使用します。おそらく、「#」文字列化演算子を使用して作成する必要があります。マクロをリテラル文字列に変換します。

#1の例:

#ifdef __TESTING
    #define IV_DOMAIN "domain.org"                        //in house testing
#Elif __LIVE_TESTING
    #define IV_DOMAIN "test.domain.com"           //live testing servers
#else
    #define IV_DOMAIN "domain.com"                        //production
#endif

// Sub-Domain
#define IV_SECURE "secure." IV_DOMAIN          //secure.domain.org etc
#define IV_MOBILE "m." IV_DOMAIN

そして、トークン貼り付け演算子に関する限り、トークン貼り付けプリプロセッサ演算子の使用を提案した回答のほとんどが実際にそれを試したとは思いません-使用するのは難しいかもしれません。

よく提案される回答を使用すると、IV_SECUREマクロを使用しようとすると、コンパイラエラーが発生します。理由は次のとおりです。

#define IV_SECURE "secure."##IV_DOMAIN

に展開:

"secure"domain.org

'# `'' stringizing '演算子を使用してみてください。

#define IV_SECURE "secure." #IV_DOMAIN

ただし、古いマクロだけでなく、マクロ引数に対してのみ機能するため、機能しません。

トークン貼り付け( '##')または文字列化( '#')前処理演算子を使用する場合に注意する必要があるのは、すべての場合に正しく機能するために、追加レベルの間接参照を使用する必要があることです。

これを行わず、トークン貼り付け演算子に渡される項目自体がマクロである場合、おそらく期待したものとは異なる結果が得られます。

#include <stdio.h>

#define STRINGIFY2( x) #x
#define STRINGIFY(x) STRINGIFY2(x)
#define PASTE2( a, b) a##b
#define PASTE( a, b) PASTE2( a, b)

#define BAD_PASTE(x,y) x##y
#define BAD_STRINGIFY(x) #x

#define SOME_MACRO function_name

int main() 
{
    printf( "buggy results:\n");
    printf( "%s\n", STRINGIFY( BAD_PASTE( SOME_MACRO, __LINE__)));
    printf( "%s\n", BAD_STRINGIFY( BAD_PASTE( SOME_MACRO, __LINE__)));
    printf( "%s\n", BAD_STRINGIFY( PASTE( SOME_MACRO, __LINE__)));

    printf( "\n" "desired result:\n");
    printf( "%s\n", STRINGIFY( PASTE( SOME_MACRO, __LINE__)));
}

出力:

buggy results:
SOME_MACRO__LINE__
BAD_PASTE( SOME_MACRO, __LINE__)
PASTE( SOME_MACRO, __LINE__)

desired result:
function_name21

したがって、元のIV_DOMAIN定義と上からのユーティリティマクロを使用して、これを実行して必要なものを取得できます。

// Sub-Domain
#define IV_SECURE "secure." STRINGIFY( IV_DOMAIN)   //secure.domain.org etc
#define IV_MOBILE "m." STRINGIFY( IV_DOMAIN)
26
Michael Burr

次に一緒にある文字列は、Cコンパイラによって結合されます。

#define DOMAIN "example.com"
#define SUBDOMAIN "test." DOMAIN
const char *asCString = SUBDOMAIN;
NSString *asNSString = @SUBDOMAIN;
8
rpetrich

私はあなたの最初の質問に対してたくさんの良い正しい答えを見ますが、あなたの2番目の質問には何もありません、それでここにこれがあります:これはひどい考えだと思います。サーバー名を変更するためだけにソフトウェア(特にリリースバージョン)を再構築する必要があるのはなぜですか?また、ソフトウェアのどのバージョンがどのサーバーを指しているかをどのようにして知ることができますか?実行時にチェックするメカニズムを組み込む必要があります。プラットフォームで実用的である場合は、構成ファイルからドメイン/ URLをロードすることをお勧めします。最小の組み込みプラットフォームだけが、その目的のために「実用的」ではないかもしれません:)

7
rmeador

##演算子を使用してみてください

#define IV_SECURE secure.##IV_DOMAIN
6
JaredPar

必要なのは、#演算子と##演算子、および自動文字列連結です。

#前処理演算子は、マクロパラメータを文字列に変換します。 ##演算子は、2つのトークン(マクロパラメーターなど)を一緒に貼り付けます。

私に思い浮かぶ可能性は

#define IV_DOMAIN domain.org
#define IV_SECURE(DOMAIN) "secure." #DOMAIN

iV_SECUREをに変更する必要があります

#define IV_SECURE "secure." "domain.org"

これは自動的に「secure.domain.org」に連結されます(翻訳のフェーズがCでC++と同じであると仮定します)。

別の編集:私がどうやって混乱したかを示すコメントを読んでください。少し錆びているかもしれませんが、私はCで十分な経験があることを覚えておいてください。この答えは削除しますが、Cプリプロセッサで混乱しやすい例として残しておきたいと思いました。

5
David Thornley

他の人が指摘しているように、トークンの貼り付けを使用します。また、次のようなマクロ名にも注意する必要があります。

__TESTING

実装のためにCで予約されています(Objective Cについてはわかりません)-独自のコードでそれらを使用することは許可されていません。予約済みの名前は、二重下線を含むもの、および下線と大文字で始まるものです。

3
anon