There’s a great keyword in C# called nameof which is used to “stringify” a variable, type, or member. It is used all the time for simple things like checking if a variable is null at the beginning of a method.

public static void PrintIntegers(IEnumerable<int> integers)
{
    // NOTE: This can be done easier in newer versions of C# using:
    // ArgumentNullException.ThrowIfNull(integers)
    if (integers == null)
    {
        throw new ArgumentNullException(nameof(integers));
    }
    
    foreach (var integer : integers)
    {
        Console.WriteLine($"{integer}");
    }
}

The nameof keyword is great. If the variable integers ever changes to something like integersToPrint, we would get a compilation error if we didn’t also update our use of the variable when throwing our ArgumentNullExcpetion. This ensures that we don’t have out-of-date strings littering our codebase and making it difficult to figure out what things mean when something goes wrong months down the line. If you use your handy dandy rename feature in an IDE then all usages of the variable get updated anyway and you don’t have to go hunt down old names inside string constants.

I want this same feature in C, which means we are writing macros!

#include <stdio.h>
#include <stddef.h>
#define NAMEOF(x) ((void)sizeof(&(x)), #x)
#define NAMEOF_TYPE(t) ((void)sizeof((t *)NULL), #t)

typedef struct foo {
  int a;
  int b;
} foo;

int main() {
  foo f = { .a = 1, .b = 2 };
  
  printf("The value of '%s' is %d\n",
         NAMEOF(f.a),
         f.a);
  printf("The value of '%s' is %d\n",
         NAMEOF(f.b),
         f.b);
  printf("The type of '%s' is '%s'\n",
         NAMEOF(f),
         NAMEOF_TYPE(foo));
}        
/* Output:
 * The value of 'f.a' is 1
 * The value of 'f.b' is 2
 * The type of 'f' is 'foo'
 */

NAMEOF and NAMEOF_TYPE generate a comma separated list of expressions. Multiple expressions can be separated by commas and only the rightmost expression is returned. For NAMEOF, we call sizeof on the address of x. Since sizeof is run at compile time, we’re not actually needing to evaluate the real address of x at runtime. We just want the type checking to occur. Taking the address of x ensures that the argument provided is an lvalue or a function designator. NAMEOF_TYPE works in a similar way being evaluated at compile time. For types, we attempt to cast NULL to a pointer of type t. If either of these initial evaluations cause problems, there will be a compilation error about an unknown variable or type respectively. I wasn’t able to figure out a way to combine the features of NAMEOF and NAMEOF_TYPE like C# does, but this works well for me now. Check out some usage of it in my project, otter.

4 responses to “A nice little nameof macro in C”

  1. This is really cool! I think that `nameof` is one of my favorite features in C#. Note that what you created is slightly different than what C# does. The `nameof` function only returns the name of the last “thing” after the last `.`. For example:

    “`
    using System;

    public class Test
    {
    public const string Foo = “Bar”;
    }

    public class Program
    {
    public static void Main()
    {
    Console.WriteLine(nameof(Test.Foo));
    }
    }
    “`

    Outputs `Foo` not `Test.Foo`.

    https://dotnetfiddle.net/0NQH8Y

    1. That’s a great point. Since the macro in C is using `#` to stringify the parameter passed to it, I think it would be pretty difficult to just grab the variable name when it’s accessed via the struct. It still generally fixes the drift between what’s in code and what you want to print but is definitely not as nice! If anyone knows more macro magic to accomplish this I’d be interested though.

    2. Neat comparison! I actually like Nathan’s implementation even better, since you would be able to see what Foo belongs to in your example.

      Nathan, it would be cool to have an explanation of what lines 3 and 4 actually do. It’s fascinating that code like “((void)sizeof(&(x)), #x)” outputs the variable name somehow.

      Cheers, guys!

      1. It’s two expressions separated by a comma. In C, you can have multiple expressions be evaluated and only the last expression will have an output that is usable. So something like const char* nameof = NAMEOF(foo) would expand to:

        const char* nameof = (void)sizeof(&foo), "foo";

        The (void)sizeof(&foo) is evaluated by the compiler, but the result of the sizeof is just thrown away (and we tell the compiler to not warn us about checking the value of that sizeof by casting it to void). So really, the end result of all this is just simply:


        const char* nameof = "foo";

Leave a Reply

Discover more from Nathaniel Wright

Subscribe now to keep reading and get access to the full archive.

Continue reading