【问题标题】:Printing name of #define by its value?按其值打印#define 的名称?
【发布时间】:2012-04-05 16:31:20
【问题描述】:

我有一个 C 程序,其中包含一些错误代码定义。像这样:

#define FILE_NOT_FOUND -2
#define FILE_INVALID -3 
#define INTERNAL_ERROR -4
#define ... 
#define ... 

是否可以通过值打印定义的名称?像这样:

PRINT_NAME(-2);

// output
FILE_NOT_FOUND

【问题讨论】:

标签: c macros c-preprocessor


【解决方案1】:

简而言之,没有。最简单的方法是这样(请注意:这假设您永远不会将错误分配为零/空):

 //Should really be wrapping numerical definitions in parentheses. 
#define FILE_NOT_FOUND  (-2)
#define FILE_INVALID    (-3) 
#define INTERNAL_ERROR  (-4)

typdef struct {
  int errorCode;
  const char* errorString;
} errorType;

const errorType[] = {
  {FILE_NOT_FOUND, "FILE_NOT_FOUND" },
  {FILE_INVALID,   "FILE_INVALID"   },
  {INTERNAL_ERROR, "INTERNAL_ERROR" },
  {NULL,           "NULL"           },
};

// Now we just need a function to perform a simple search
int errorIndex(int errorValue) {
  int i;
  bool found = false;
  for(i=0; errorType[i] != NULL; i++) {
    if(errorType[i].errorCode == errorValue) {
      //Found the correct error index value
      found = true;
      break;
    }
  }
  if(found) {
    printf("Error number: %d (%s) found at index %d",errorType[i].errorCode, errorType[i].errorString, i);
  } else {
    printf("Invalid error code provided!");
  }
  if(found) {
    return i;
  } else {
    return -1;
  }
}

享受吧!

此外,如果您想节省更多打字时间,可以使用预处理器宏使其更加整洁:

#define NEW_ERROR_TYPE(ERR) {ERR, #ERR}
 const errorType[] = {
      NEW_ERROR_TYPE(FILE_NOT_FOUND),
      NEW_ERROR_TYPE(FILE_INVALID),
      NEW_ERROR_TYPE(INTERNAL_ERROR),
      NEW_ERROR_TYPE(NULL)
    };

现在您只需键入一次宏名称,减少了拼写错误的机会。

【讨论】:

    【解决方案2】:

    你可以这样做。

    #include <stdio.h>
    
    #define FILE_NOT_FOUND -2
    #define FILE_INVALID -3 
    #define INTERNAL_ERROR -4
    
    const char* name(int value) {
    #define NAME(ERR) case ERR: return #ERR;
        switch (value) {
            NAME(FILE_NOT_FOUND)
            NAME(FILE_INVALID)
            NAME(INTERNAL_ERROR)
        }
        return "unknown";
    #undef NAME
    }
    
    int main() {
        printf("==== %d %s %s\n", FILE_NOT_FOUND, name(FILE_NOT_FOUND), name(-2));
    }
    

    【讨论】:

    • 我最喜欢这个版本,因为与基于数组的解决方案不同,如果您碰巧有不明确/重复的错误代码,它会给您一个编译器错误。
    【解决方案3】:

    不,这是不可能的。这会打印什么?

    #define FILE_NOT_FOUND   1
    #define UNIT_COST        1
    #define EGGS_PER_RATCHET 1
    
    PRINT_NAME(1);
    

    【讨论】:

      【解决方案4】:

      有点...

      #define ERROR_CODE_1 "FILE_NOT_FOUND"
      #define ERROR_CODE_2 "FILE_FOUND"
      
      #define PRINT_NAME(N) ERROR_CODE_ ## N
      

      或:

      static char* error_codes(int err) {
         static char name[256][256] = {
      
         };
         int base = .... lowest error code;
         return name[err - base];
      }
      
      #define PRINT_NAME(N) error_code(N)
      

      【讨论】:

        【解决方案5】:

        为什么不选择使用枚举呢?

        enum errors {FILE_NOT_FOUND = -2, FILE_INVALID = -3, INTERNAL_ERROR = -4};
        
        FILE *fp = fopen("file.txt", "r");
        
        
        if(fp == NULL) {
            printf("Error\n");
            exit(FILE_NOT_FOUND);
        }
        

        【讨论】:

          【解决方案6】:

          不是自动的。名称在编译过程中丢失,代码中只保留常数。

          但是你可以构建这样的东西:

          const char * a[] = {"","","FILE_NOT_FOUND","FILE_INVALID"};
          

          并通过使用定义值绝对值作为索引来访问它。

          【讨论】:

            【解决方案7】:

            为此使用 C99 的指定初始化程序,但如果您的错误代码为负数,则需要小心。

            首先是正值的版本:

            #define CODE(C) [C] = #C
            
            static
            char const*const codeArray[] = {
            CODE(EONE),
            CODE(ETWO),
            CODE(ETHREE),
            };
            
            enum { maxCode = (sizeof codeArray/ sizeof codeArray[0]) };
            

            这会分配一个具有您需要的长度的数组,并且字符串指针位于正确的位置。请注意,标准允许重复值,最后一个是实际存储在数组中的那个。

            要打印错误代码,您必须检查索引是否小于maxCode

            如果您的错误代码始终为负数,您只需在打印前取反代码即可。但反过来可能是个好主意:让代码为正数并检查其符号的返回值。如果它是负数,则错误代码将是该值的否定。

            【讨论】:

              【解决方案8】:

              这就是我在 C 中的做法:

              #pragma once 
              
              #ifdef DECLARE_DEFINE_NAMES
              // Switch-case macro for getting defines names
              #define BEGIN_DEFINE_LIST  const char* GetDefineName (int key) { switch (key) {
              #define MY_DEFINE(name, value)  case value: return #name;
              #define END_DEFINE_LIST } return "Unknown"; }
              
              #else
              
              // Macros for declaring defines
              #define BEGIN_COMMAND_LIST /* nothing */
              #define MY_DEFINE(name, value)  static const int name = value;
              #define END_COMMAND_LIST /* nothing */
              
              #endif
              
              // Declare your defines
              BEGIN_DEFINE_LIST
              
              MY_DEFINE(SUCCEEDED,       0)
              MY_DEFINE(FAILED,         -1)
              MY_DEFINE(FILE_NOT_FOUND, -2)
              MY_DEFINE(INVALID_FILE,   -3)
              MY_DEFINE(INTERNAL_ERROR  -4)
              etc...
              
              END_DEFINE_LIST
              

              #pragma once
              const char* GetDefineName(int key);
              

              #define DECLARE_DEFINE_NAMES
              #include "MyDefines.h"
              

              现在,您可以像这样在任何地方使用声明的 switch-case 宏:

              #include "MyDefines.h"
              #include "MyDefineInfo.h"
              
              void PrintThings()
              {
                  Print(GetDefineName(SUCCEEDED));
                  Print(GetDefineName(INTERNAL_ERROR));
                  Print(GetDefineName(-1);
                  // etc.
              }
              

              【讨论】:

                猜你喜欢
                • 1970-01-01
                • 1970-01-01
                • 2013-07-12
                • 2010-11-12
                • 2016-10-10
                • 2017-12-20
                • 2010-10-21
                • 2017-05-28
                • 2015-01-08
                相关资源
                最近更新 更多