Library / SDK
Neargye/nameof avatar
Neargye/nameof

nameof: a C++17 header-only library that turns identifiers into their own names

Nameof operator for modern C++, simply obtain the name of a variable, type, function, macro, and enum

2,368 stars126 forksC++MIT

At a glance

What is it?
A single header file of macros and functions that recover the name of a variable, type, function, macro or enum at compile time, plus the release notes that show its real limitations.
Who is it for?
nameof earns its place in a codebase the moment you write one switch statement, one serialization format or one log line that has to name a field. The costs are honest and small: a compiler that is not Clang, GCC or MSVC means no macro support, and exact type spellings vary between them, which is why the library offers short names and RTTI based variants alongside the exact one.
Can I use it commercially?
Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository last received commits 6 days ago.
What is it written in?
Mainly C++, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on October 11, 2026, and from our analysis. They are not legal advice.

Editorial analysis

Reflection C++ does not have, recovered from the preprocessor

C++ has no reflection, so a library that wants names has to go around the language. nameof does it with a macro that stringifies a token the compiler has not yet expanded, which means the name it returns is the name as you wrote it. For a variable that is the obvious behavior:

cpp
  // Name of variable.
  NAMEOF(somevar) -> "somevar"

  // Name of member variable.
  NAMEOF(person.address.zip_code) -> "zip_code"

The member case is the interesting one. You get the leaf field name, not the whole path, which is exactly what a serializer or a form library wants and exactly what a hand written string would get wrong the first time somebody renames a field.

Functions work the same way, including templates, where the type arguments are dropped and only the function name comes back. There is a full variant that keeps the template arguments and a raw variant that returns the expression text exactly as written, including the call parentheses, which is the hook you would use to build a fully qualified path for an error message. Macros are covered too, so `NAMEOF(__LINE__)` returns the literal text of the macro name.

The whole library is one header, `include/nameof.hpp`, and it is MIT licensed with 2,364 stars, 125 forks and zero open issues. It was last pushed on 2026-08-28, so it is actively maintained.

Enum names, flag combinations and the fallback you should use

Enum to string is where most hand rolled helpers break, and it is the feature with the most variants here. Given a scoped enum, you get the enumerator name:

cpp
  auto color = Color::RED;
  // Name of enum value.
  NAMEOF_ENUM(color) -> "RED"
  nameof::nameof_enum(color) -> "RED"

If the value is known at compile time there is a constant version, `NAMEOF_ENUM_CONST`, and the README makes a point about it: the compile time cost is lower and it is not restricted by what the docs call the enum_range limitation, which is the bound on how many enumerators the runtime version will search.

Flags are handled by joining names with a separator, defaulting to a pipe:

cpp
  enum AnimalFlags { HasClaws = 1, CanFly = 2, EatsFish = 4 };

  // Enum flag value to string.
  auto flag = static_cast<AnimalFlags>(CanFly | EatsFish);
  NAMEOF_ENUM_FLAG(flag) -> "CanFly|EatsFish"
  nameof::nameof_enum_flag(flag, '$') -> "CanFly$EatsFish"

The variant worth remembering for configuration driven code is the `OR` form, which takes a default for values that have no name:

cpp
  NAMEOF_ENUM_OR(Color::GREEN, "none") -> "GREEN"
  NAMEOF_ENUM_OR((Color)0, "none") -> "none"

That second line is the honest case. A value cast from an integer that never appeared in the enum has no name to return, and without the fallback you get an empty string or undefined behavior depending on the version. If your enums come from a database or a network protocol, use this one.

Type names, and why exact spellings differ between compilers

The type functions are the part of the library where the README is most careful, and the reason is stated plainly: exact type spellings are compiler specific. The same variable can produce different strings from Clang and MSVC, so the library offers four levels of detail for each of an expression and a type.

cpp
  const my::detail::SomeClass<int>& var_ref = var;
  // Name of variable type.
  NAMEOF_TYPE_EXPR(var_ref) -> "my::detail::SomeClass<int>"
  NAMEOF_FULL_TYPE_EXPR(var_ref) -> "const my::detail::SomeClass<int>&"
  nameof::nameof_short_type<decltype(var_ref)>() -> "SomeClass"

`TYPE` drops the `const` and the reference, `FULL_TYPE` keeps them, and `SHORT_TYPE` strips the namespace. Each has a function form taking a type as a template argument, so you can use it with `decltype` or with a `using` alias. The `EXPR` variants take the variable directly, which is more convenient when you want to avoid writing `decltype` yourself.

Two more families exist for cases the above cannot reach. The RTTI variants, `NAMEOF_TYPE_RTTI` and its full and short forms, get the name from runtime type information and therefore give the dynamic type, so a pointer to a base that actually holds a derived reports `my::detail::Derived`. That is more accurate and costs a runtime type lookup, which is why it is opt in.

For members and pointers the library provides `NAMEOF_MEMBER(&A::this_is_the_name)` to get a member name from a pointer to member, and `NAMEOF_POINTER` for functions, globals and class statics, including when you only have a `constexpr` pointer to the global.

One header, six package managers, and a documented compiler floor

Distribution is the least interesting part and the easiest to get wrong, so it is worth listing. The simplest route is dropping `include/nameof.hpp` into the project. If you prefer a build system, there is a CMake package:

cmake
find_package(nameof CONFIG REQUIRED)
target_link_libraries(your_target PRIVATE nameof::nameof)

There is also vcpkg, where the command is `vcpkg install nameof`, Conan, which takes `nameof/x.y.z` in its requires section with an explicit version, AUR for anyone on Arch, and CPM for projects that fetch dependencies by git tag:

cmake
CPMAddPackage(
    NAME nameof
    GITHUB_REPOSITORY Neargye/nameof
    GIT_TAG vx.y.z # Replace `x.y.z` with the release version you want to use.
)

The repository has a `CMakeLists.txt` and a `cmake/` directory for the package config, and the README badges confirm both vcpkg and Conan recipes exist upstream rather than being maintained by third parties.

The compiler requirement is C++17 or newer, and the supported list is short enough to matter: Clang or LLVM 5 and up, GCC 9 and up, Visual Studio 2017 and up. Anything outside that list means the macro based features do not work. The README also points at the reference documentation for feature specific requirements, because not every function needs every compiler: the RTTI variants and the type name functions have their own constraints. The repository topics list `header-only`, `single-file`, `reflection` and `metaprogramming`, which is an accurate self description of what is going on.

Limitations have their own documentation page

Most libraries of this kind bury the bad news. This one has a dedicated `doc/limitations.md` linked from the documentation list, alongside `doc/reference.md`. That is a small signal worth weighting, because the limitations of a reflection substitute are not edge cases, they are the whole design.

The `enum_range` limitation is named in the README itself, in the comment on `NAMEOF_ENUM_CONST` that says the constant version is not restricted by it. The implication is that the runtime enum name lookup searches a bounded range of underlying values rather than an unbounded set, which matters if you have a sparse enum with values up in the thousands. The `NAMEOF_ENUM_OR` fallback exists to cover values outside any name, whether because of that range or because the value was constructed from raw data.

Type names have the other documented constraint, the compiler specific spelling. It is not a bug so much as a consequence of the approach, since the string comes from the compiler's own mangled or demangled representation. If you need a name that is stable across toolchains and across machines, the answer is an explicit table or a code generation step, and no amount of macro trickery changes that. The library is for logging, diagnostics, serialization keys and test failure messages, where the name only has to be readable in the environment where the failure happened.

The repository layout backs this up: `example/` holds runnable files including `example_custom_name.cpp` linked from the README, `test/` holds the test suite, and `doc/` holds both the reference and the limitations page.

A long release history with recent work on string storage

The release record shows a project that has been shipping for years and is still fixing sharp edges. The most recent release is v0.10.6 on 2026-07-04, and its change list is about correctness rather than features: fixing `cstring` lifetime and null termination, adding `std::format` and `fmt` support for `cstring`, fixing RTTI type name demangling, and fixing stable string storage for enum, type, member and pointer names.

That last item deserves attention. Returning a `std::string` that must remain valid for the life of the program means the library keeps its own storage, and bugs there are the kind that show up as a dangling pointer in a long running service rather than as a failing test. A release devoted to that is a good sign about how the maintainer thinks about the failure modes.

The release before it, v0.10.5 on 2026-05-03, added `cstring` in the first place to allow it as a non type template parameter, which is a technique for putting a string into a template argument without it becoming a pointer. That is a niche feature and it is the kind of thing that only gets added when someone needs it for a real problem, then generalized for everyone.

Before those, v0.10.4 landed on 2024-01-31, so the project went quiet for a stretch and then resumed. There is a Compiler Explorer badge at the top of the README, which is a good habit for a library whose whole mechanism depends on how a particular compiler expands a macro: you can paste in your own compiler and see exactly what happens.

Editorial conclusion

nameof earns its place in a codebase the moment you write one switch statement, one serialization format or one log line that has to name a field. The costs are honest and small: a compiler that is not Clang, GCC or MSVC means no macro support, and exact type spellings vary between them, which is why the library offers short names and RTTI based variants alongside the exact one. Add `include/nameof.hpp` and start with `NAMEOF` and `NAMEOF_ENUM`; only reach for the type family once you have confirmed your compiler spells the type the way your tests expect.

Frequently asked questions

What is nameof in C++?

nameof is a header-only C++17 library of macros and functions that returns the name of a variable, type, function, macro or enum value as a string. It works around the lack of reflection in C++ by capturing the identifier text before the compiler expands it.

How do I get the name of an enum value in C++?

Use NAMEOF_ENUM for a runtime value or NAMEOF_ENUM_CONST for one known at compile time, which costs less to compile and is not limited by the enum_range bound. For flags, NAMEOF_ENUM_FLAG joins names with a separator, and NAMEOF_ENUM_OR takes a fallback string for values that have no enumerator name.

Which compilers does the nameof library support?

It requires C++17 or newer and supports Clang or LLVM 5 and up, GCC 9 and up, and Visual Studio 2017 and up. Exact type name spellings differ between those compilers, which is why the library also provides short name and RTTI based variants.

How do I add nameof to my project?

The simplest option is to copy include/nameof.hpp into your project. There is also a CMake package used with find_package and target_link_libraries, plus vcpkg, Conan, AUR and CPM integrations, all documented in the README.

Official sources

  1. Issues
  2. License: MIT
  3. Neargye/nameof on GitHub
  4. README
  5. Releases
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/neargye-nameof.svg)](https://hysenlabs.com/projects/neargye-nameof)