web-dev-qa-db-fra.com

Comment créer une chaîne de caractères à partir de la valeur d'une macro C?

Par exemple, comment éviter d'écrire deux fois 'func_name'?

#ifndef TEST_FUN
#  define TEST_FUN func_name
#  define TEST_FUN_NAME "func_name"
#endif

Je voudrais suivre la règle Single Point of Truth .

Version du préprocesseur C:

$ cpp --version
cpp (GCC) 4.1.2 20070626 (Red Hat 4.1.2-14)
49
jfs

Celui qui est timide* vous a donné le germe d'un réponse , mais seulement le germe. La technique de base pour convertir une valeur en chaîne dans le pré-processeur C est en effet via l'opérateur '#', mais une simple translittération de la solution proposée obtient une erreur de compilation:

#define TEST_FUNC test_func
#define TEST_FUNC_NAME #TEST_FUNC

#include <stdio.h>
int main(void)
{
    puts(TEST_FUNC_NAME);
    return(0);
}

L'erreur de syntaxe se trouve sur la ligne 'met ()' - le problème est un 'errant #' dans la source.

Dans la section 6.10.3.2 de la norme C, "L'opérateur #", il est écrit:

Chaque jeton de prétraitement de la liste de remplacement pour une macro de type fonction doit être suivi d'un paramètre comme prochain jeton de prétraitement de la liste de remplacement.

Le problème est que vous pouvez convertir des arguments de macro en chaînes - mais vous ne pouvez pas convertir des éléments aléatoires qui ne sont pas des arguments de macro.

Donc, pour obtenir l'effet que vous recherchez, vous devez très certainement faire un travail supplémentaire.

#define FUNCTION_NAME(name) #name
#define TEST_FUNC_NAME  FUNCTION_NAME(test_func)

#include <stdio.h>

int main(void)
{
    puts(TEST_FUNC_NAME);
    return(0);
}

Je ne suis pas tout à fait clair sur la façon dont vous prévoyez d'utiliser les macros et comment vous prévoyez d'éviter complètement la répétition. Cet exemple légèrement plus élaboré pourrait être plus informatif. L'utilisation d'une macro équivalente à STR_VALUE est un idiome qui est nécessaire pour obtenir le résultat souhaité.

#define STR_VALUE(arg)      #arg
#define FUNCTION_NAME(name) STR_VALUE(name)

#define TEST_FUNC      test_func
#define TEST_FUNC_NAME FUNCTION_NAME(TEST_FUNC)

#include <stdio.h>

static void TEST_FUNC(void)
{
    printf("In function %s\n", TEST_FUNC_NAME);
}

int main(void)
{
    puts(TEST_FUNC_NAME);
    TEST_FUNC();
    return(0);
}

* Au moment où cette réponse a été écrite pour la première fois, le nom de shoosh utilisait 'Timide' dans le nom.

71
Jonathan Leffler

@Jonathan Leffler: Merci. Votre solution fonctionne.

Un exemple de travail complet:

/** compile-time dispatch 

   $ gcc -Wall -DTEST_FUN=another_func macro_sub.c -o macro_sub && ./macro_sub
*/
#include <stdio.h>

#define QUOTE(name) #name
#define STR(macro) QUOTE(macro)

#ifndef TEST_FUN
#  define TEST_FUN some_func
#endif

#define TEST_FUN_NAME STR(TEST_FUN)

void some_func(void)
{
  printf("some_func() called\n");
}

void another_func(void)
{
  printf("do something else\n");
}

int main(void)
{
  TEST_FUN();
  printf("TEST_FUN_NAME=%s\n", TEST_FUN_NAME);
  return 0;
}

Exemple:

$ gcc -Wall -DTEST_FUN=another_func macro_sub.c -o macro_sub && ./macro_sub
do something else
TEST_FUN_NAME=another_func
16
jfs
#include <stdio.h>

#define QUOTEME(x) #x

#ifndef TEST_FUN
#  define TEST_FUN func_name
#  define TEST_FUN_NAME QUOTEME(TEST_FUN)
#endif

int main(void)
{
    puts(TEST_FUN_NAME);
    return 0;
}

Référence: Wikipédia préprocesseur C page

0
Jason Weathered