Anton Horst
Anton Horst

Reputation: 273

Printing name of #define by its value?

I have a C program with some definitions for error codes. Like this:

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

Is it possible to print the name of the definition by its value? Like this:

PRINT_NAME(-2);

// output
FILE_NOT_FOUND

Upvotes: 10

Views: 13124

Answers (8)

Ziffusion
Ziffusion

Reputation: 8913

You can do something like this.

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

Upvotes: 6

JKallio
JKallio

Reputation: 903

This is how I do it in C:

< MyDefines.h >

#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

< MyDefineInfo.h >

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

< MyDefineInfo.c >

#define DECLARE_DEFINE_NAMES
#include "MyDefines.h"

Now, you can use the declared switch-case macro wherever like this:

< WhereEver.c >

#include "MyDefines.h"
#include "MyDefineInfo.h"

void PrintThings()
{
    Print(GetDefineName(SUCCEEDED));
    Print(GetDefineName(INTERNAL_ERROR));
    Print(GetDefineName(-1);
    // etc.
}

Upvotes: 0

Jens Gustedt
Jens Gustedt

Reputation: 78903

Use designated initializers of C99 for this, but a bit of care is necessary if your error codes are negative.

First a version for positive values:

#define CODE(C) [C] = #C

static
char const*const codeArray[] = {
CODE(EONE),
CODE(ETWO),
CODE(ETHREE),
};

enum { maxCode = (sizeof codeArray/ sizeof codeArray[0]) };

This allocates an array with the length that you need and with the string pointers at the right positions. Note that duplicate values are allowed by the standard, the last one would be the one that is actually stored in the array.

To print an error code, you'd have to check if the index is smaller than maxCode.

If your error codes are always negative you'd just have to negate the code before printing. But it is probably a good idea to do it the other way round: have the codes to be positive and check a return value for its sign. If it is negative the error code would be the negation of the value.

Upvotes: 1

Cloud
Cloud

Reputation: 19333

In short, no. The easiest way to do this would be something like so (PLEASE NOTE: this assumes that you can never have an error assigned to zero/null):

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

Enjoy!

Additionally, if you wanted to save on typing even more, you could use a preprocessor macro to make it even neater:

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

Now you only have to type the macro name once, reducing the chance of typos.

Upvotes: 6

Flynch
Flynch

Reputation: 84

Not automatically. The name is losing during compilation, and only the constant number remains in the code.

But you can build something like this:

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

and access it by using the define value absolute value as index.

Upvotes: 1

Makoto
Makoto

Reputation: 106389

Why not elect to use an enumeration instead?

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

Upvotes: 1

Anycorn
Anycorn

Reputation: 51435

Kinda ...

#define ERROR_CODE_1 "FILE_NOT_FOUND"
#define ERROR_CODE_2 "FILE_FOUND"

#define PRINT_NAME(N) ERROR_CODE_ ## N

or:

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)

Upvotes: 2

RichieHindle
RichieHindle

Reputation: 281365

No, that's not possible. What would this print?

#define FILE_NOT_FOUND   1
#define UNIT_COST        1
#define EGGS_PER_RATCHET 1

PRINT_NAME(1);

Upvotes: 2

Related Questions