Is it possible to ASSERT_DOES_NOT_COMPILE with GTest? - c++

Assume a template class where we assert at compile time that the integer template argument must be greater zero:
template<int N>
class A
{
public:
A() {
static_assert(N > 0, "N needs to be greater 0.");
}
};
Is it possible to create a googletest unit test that compiles, but reports the error at runtime? For example:
TEST(TestA, ConstructionNotAllowedWithZero)
{
ASSERT_DOES_NOT_COMPILE(
{
A< 0 > a;
}
);
}

There is a way, but sadly it's probably not the way you want.
My first thought was to try to get SFINAE to discount an overload by expanding an invalid lambda in an unevaluated context. Unhappily (in your case), this is specifically disallowed...
#define CODE { \
utter garbage \
}
struct test
{
template<class T>
static std::false_type try_compile(...) { return{}; }
template<class T>
static auto try_compile(int)
-> decltype([]() CODE, void(), std::true_type());
{ return {}; }
};
struct tag {};
using does_compile = decltype(test::try_compile<tag>(0));
output:
./maybe_compile.cpp:88:17: error: lambda expression in an unevaluated operand
-> decltype([]() CODE, void(), std::true_type());
So it was back to the drawing board and a good old system call to call out to the compiler...
#include <iostream>
#include <string>
#include <cstdlib>
#include <fstream>
#include <sstream>
struct temp_file {
temp_file()
: filename(std::tmpnam(nullptr))
{}
~temp_file() {
std::remove(filename.c_str());
}
std::string filename;
};
bool compiles(const std::string code, std::ostream& reasons)
{
using namespace std::string_literals;
temp_file capture_file;
temp_file cpp_file;
std::ofstream of(cpp_file.filename);
std::copy(std::begin(code), std::end(code), std::ostream_iterator<char>(of));
of.flush();
of.close();
const auto cmd_line = "c++ -x c++ -o /dev/null "s + cpp_file.filename + " 2> " + capture_file.filename;
auto val = system(cmd_line.c_str());
std::ifstream ifs(capture_file.filename);
reasons << ifs.rdbuf();
ifs.close();
return val == 0;
}
auto main() -> int
{
std::stringstream reasons1;
const auto code1 =
R"code(
#include <iostream>
int main() {
return 0;
}
)code";
std::cout << "compiles: " << compiles(code1, reasons1) << std::endl;
std::stringstream reasons2;
const auto code2 =
R"code(
#include <iostream>
int main() {
FOO!!!!XC#£$%^&*()VBNMYGHH
return 0;
}
)code";
std::cout << "compiles: " << compiles(code2, reasons2) << std::endl;
std::cout << "\nAnd here's why...\n";
std::cout << reasons2.str() << std::endl;
return 0;
}
which in my case gives the following example output:
compiles: 1
compiles: 0
And here's why...
/var/tmp/tmp.3.2dADZ7:4:9: error: use of undeclared identifier 'FOO'
FOO!!!!XC#£$%^&*()VBNMYGHH
^
/var/tmp/tmp.3.2dADZ7:4:19: error: non-ASCII characters are not allowed outside of literals and identifiers
FOO!!!!XC#£$%^&*()VBNMYGHH
^
2 errors generated.
of course you can add all the necessary macros around the call to compiles() in order to GTESTify it. You will of course have to set command line options on the c-compiler invocation to set the correct paths and defines.

Related

How to use u8_to_u32_iterator in Boost Spirit X3?

I am using Boost Spirit X3 to create a programming language, but when I try to support Unicode, I get an error!
Here is an example of a simplified version of that program.
#define BOOST_SPIRIT_X3_UNICODE
#include <boost/spirit/home/x3.hpp>
namespace x3 = boost::spirit::x3;
struct sample : x3::symbols<unsigned> {
sample()
{
add("48", 10);
}
};
int main()
{
const std::string s("🌸");
boost::u8_to_u32_iterator<std::string::const_iterator> first{cbegin(s)},
last{cend(s)};
x3::parse(first, last, sample{});
}
Live on wandbox
What should I do?
As you noticed, internally char_encoding::unicode employs char32_t.
So, first changing the symbols accordingly:
template <typename T>
using symbols = x3::symbols_parser<boost::spirit::char_encoding::unicode, T>;
struct sample : symbols<unsigned> {
sample() { add(U"48", 10); }
};
Now the code fails calling into case_compare:
/home/sehe/custom/boost_1_78_0/boost/spirit/home/x3/string/detail/tst.hpp|74 col 33| error: no match for call to ‘(boost::spirit::x3::case_compare<boost::spirit::char_encoding::unicode>) (reference, char32_t&)’
As you can see it expects a char32_t reference, but u8_to_u32_iterator returns unsigned ints (std::uint32_t).
Just for comparison / sanity check: https://godbolt.org/z/1zozxq96W
Luckily you can instruct the u8_to_u32_iterator to use another co-domain type:
Live On Compiler Explorer
#define BOOST_SPIRIT_X3_UNICODE
#include <boost/spirit/home/x3.hpp>
#include <iomanip>
#include <iostream>
namespace x3 = boost::spirit::x3;
template <typename T>
using symbols = x3::symbols_parser<boost::spirit::char_encoding::unicode, T>;
struct sample : symbols<unsigned> {
sample() { add(U"48", 10)(U"🌸", 11); }
};
int main() {
auto test = [](auto const& s) {
boost::u8_to_u32_iterator<decltype(cbegin(s)), char32_t> first{
cbegin(s)},
last{cend(s)};
unsigned parsed_value;
if (x3::parse(first, last, sample{}, parsed_value)) {
std::cout << s << " -> " << parsed_value << "\n";
} else {
std::cout << s << " FAIL\n";
}
};
for (std::string s : {"🌸", "48", "🤷"})
test(s);
}
Prints
🌸 -> 11
48 -> 10
🤷 FAIL

Getting field names with boost::pfr

Hi I'm using boost::pfr for basic reflection, it works fine, but the problem is it is only print or deal with the field values, like with boost::pfr::io it prints each member of the struct, but how can I print it as name value pairs, same issue with for_each_field, the functor only accepts values, but not names. How can I get the field names?
struct S {
int n;
std::string name;
};
S o{1, "foo"};
std::cout << boost::pfr::io(o);
// Outputs: {1, "foo"}, how can I get n = 1, name = "foo"?
If you think adapting a struct is not too intrusive (it doesn't change your existing definitions, and you don't even need to have it in a public header):
BOOST_FUSION_ADAPT_STRUCT(S, n, name)
Then you can concoct a general operator<< for sequences:
namespace BF = boost::fusion;
template <typename T,
typename Enable = std::enable_if_t<
// BF::traits::is_sequence<T>::type::value>
std::is_same_v<BF::struct_tag, typename BF::traits::tag_of<T>::type>>>
std::ostream& operator<<(std::ostream& os, T const& v)
{
bool first = true;
auto visitor = [&]<size_t I>() {
os << (std::exchange(first, false) ? "" : ", ")
<< BF::extension::struct_member_name<T, I>::call()
<< " = " << BF::at_c<I>(v);
};
// visit members
[&]<size_t... II>(std::index_sequence<II...>)
{
return ((visitor.template operator()<II>(), ...);
}
(std::make_index_sequence<BF::result_of::size<T>::type::value>{});
return os;
}
(Prior to c++20 this would require some explicit template types instead of the lambdas, perhaps making it more readable. I guess I'm lazy...)
Here's a live demo: Live On Compiler Explorer
n = 1, name = foo
Bonus: Correctly quoting string-like types
Live On Compiler Explorer
#include <boost/fusion/include/adapt_struct.hpp>
#include <boost/fusion/include/for_each.hpp>
#include <boost/fusion/include/at_c.hpp>
#include <iostream>
#include <iomanip>
namespace MyLib {
struct S {
int n;
std::string name;
};
namespace BF = boost::fusion;
static auto inline pretty(std::string_view sv) { return std::quoted(sv); }
template <typename T,
typename Enable = std::enable_if_t<
not std::is_constructible_v<std::string_view, T const&>>>
static inline T const& pretty(T const& v)
{
return v;
}
template <typename T,
typename Enable = std::enable_if_t<
// BF::traits::is_sequence<T>::type::value>
std::is_same_v<BF::struct_tag, typename BF::traits::tag_of<T>::type>>>
std::ostream& operator<<(std::ostream& os, T const& v)
{
bool first = true;
auto visitor = [&]<size_t I>() {
os << (std::exchange(first, false) ? "" : ", ")
<< BF::extension::struct_member_name<T, I>::call()
<< " = " << pretty(BF::at_c<I>(v));
};
// visit members
[&]<size_t... II>(std::index_sequence<II...>)
{
return (visitor.template operator()<II>(), ...);
}
(std::make_index_sequence<BF::result_of::size<T>::type::value>{});
return os;
}
} // namespace MyLib
BOOST_FUSION_ADAPT_STRUCT(MyLib::S, n, name)
int main()
{
MyLib::S o{1, "foo"};
std::cout << o << "\n";
}
Outputs:
n = 1, name = "foo"
The library cannot offer any such functionality because it is currently impossible to obtain the name of a member of a class as value of an object.
If you want to output field names, you need to declare string objects mapped with the members and implement a operator<< which uses these strings manually.
To do this a more sophisticated reflection library would probably offer macros to use in the definition of the members. Macros can expand their argument(s) into a declaration using the provided name as identifier while also producing code using the name as string literal (via the # macro replacement operator).
It's stupid but hey, with a stringifying macro per field it could be enough for you.
C++14, no additional library
#include <boost/pfr.hpp>
struct S
{
int n;
std::string name;
static char const* const s_memNames[2];
};
char const* const S::s_memNames[2] = {"n", "name"};
// utility
template< size_t I, typename TR >
char const* MemberName()
{
using T = std::remove_reference_t<TR>;
if (I < std::size(T::s_memNames))
return T::s_memNames[I];
return nullptr;
}
// test:
#include <iostream>
using std::cout;
template< size_t I, typename T >
void StreamAt(T&& inst)
{
char const* n = MemberName<I,T>();
auto& v = boost::pfr::get<I>(inst);
cout << "(" << n << " = " << v << ")";
}
int main()
{
S s{2, "boo"};
boost::pfr::for_each_field(s, [&](const auto&, auto I)
{
StreamAt<decltype(I)::value>(s);
cout << "\n";
});
}
output:
(n = 2)
(name = boo)
(previous version of the suggestion, this one has more fluff so less interesting)
#include <boost/pfr.hpp>
// library additions:
static char const* g_names[100];
template< size_t V >
struct Id : std::integral_constant<size_t, V > {};
template< size_t I, typename T >
using TypeAt = boost::pfr::tuple_element_t<I, T>;
template<std::size_t Pos, class Struct>
constexpr int Ni() // name index
{
return std::tuple_element_t<Pos, typename std::remove_reference_t<Struct>::NamesAt >::value;
}
struct StaticCaller
{
template< typename Functor >
StaticCaller(Functor f) { f();}
};
///
/// YOUR CODE HERE
struct S
{
using NamesAt = std::tuple<Id<__COUNTER__>, Id<__COUNTER__>>; // add this
int n;
std::string name;
static void Init() // add this
{
g_names[Ni<0,S>()] = "n";
g_names[Ni<1,S>()] = "name";
}
};
StaticCaller g_sc__LINE__(S::Init); // add this
// utilities
template< size_t I, typename T >
auto GetValueName(T&& inst)
{
return std::make_pair(boost::pfr::get<I>(inst), g_names[Ni<I,T>()]);
}
// test:
#include <iostream>
using std::cout;
template< size_t I, typename T >
void StreamAt(T&& inst)
{
auto const& [v,n] = GetValueName<I>(inst);
cout << "(" << v << ", " << n << ")";
}
int main()
{
S s{2, "boo"};
boost::pfr::for_each_field(s, [&](const auto&, auto I)
{
StreamAt<decltype(I)::value>(s);
cout << "\n";
});
}
output
(2, n)
(boo, name)

how to use the PEGTL parser with a separate lexer?

I have already a lexer and want to use my own token types as the input for a PEGTL parser. As an example, here is a modified version of the sum example, which reads from stdin:
#include <cstdlib>
#include <iostream>
#include <sstream>
#include <string>
#include <tao/pegtl.hpp>
using namespace TAO_PEGTL_NAMESPACE;
namespace sum
{
struct num : seq< plus< digit > > {};
struct int_list
: list< num, one< ',' > >
{};
struct grammar
: seq< int_list, eof >
{};
template< typename Rule >
struct action
{};
template<>
struct action< num >
{
template< typename ActionInput >
static void apply( const ActionInput& in, int& sum )
{
sum += atoi(in.string().c_str());
}
};
} // namespace sum
struct Token {
typedef enum { COMMA, NUM, END_OF_FILE } Type;
Token(Type type, int num = 0) : type(type), num(num) {}
Type type;
int num;
};
int main()
{
// this works, can be called like this:
// echo -n "1,2,3" | ./a.out
int d = 0.0;
if( parse< sum::grammar, sum::action >( istream_input(std::cin, 16, "stdin"), d )) {
std::cout << "parsing OK; sum = " << d << std::endl;
}
else {
std::cout << "parsing failed" << std::endl;
}
// how can I do this when I have already the tokens in a vector?
std::vector<Token> tokens;
tokens.push_back(Token(Token::Type::NUM, 1));
tokens.push_back(Token(Token::Type::COMMA));
tokens.push_back(Token(Token::Type::NUM, 2));
tokens.push_back(Token(Token::Type::COMMA));
tokens.push_back(Token(Token::Type::NUM, 3));
tokens.push_back(Token(Token::Type::END_OF_FILE));
}
It can be compiled like this: g++ -std=c++17 -I PEGTL/include parser.cc. The files in PEGTL/include are from the PEGTL repository.
How do I need to modify the parser so that the vector of tokens are used instead of std::cin? I think I have to write my own ParseInput struct, but I couldn't define all required methods and types for it. And how can I use then the token objects in the parser for this sum example?

Clang - Getting SubstTemplateTypeParm full template information

I am traversing a clang AST, but am having trouble getting the desired information when traversing the type information of a declaration of the AST that contains a clang::SubstTemplateTypeParmType.
Given the following minimal input code to clang tooling
#include <map>
template <typename K, typename V> using Map = std::map<K, V>;
using String = std::string;
using ParameterMap = Map<String, String>;
ParameterMap someFunc();
When recursing through ParameterMap's type, clang says that the first Map parameter arg, String, is a clang::SubstTemplateTypeParmType. If I try to recurse further to get more info about String, by either desugaring, or getting the replacement type (code below), the underlying type is of Type::Record, and is std::basic_string. This is unexpected for me, as I would expect the underlying type to be a template specialization, something like basic_string<const char*, std::char_traits<const char*>, std::allocator<const char*>>. Since the node is a record, I can get that the map contains a std::basic_string, but cannot get the template information of basic_string. How can I get the template specialization information of basic_string in such a case?
case Type::SubstTemplateTypeParm:
{
auto substTemplateType = qualType->getAs<SubstTemplateTypeParmType>();
walkType(substTemplateType->getReplacementType());
return;
}
I understand the requirement of posting a minimal runnable code example, which is below. However this requires a clang tooling dependency, for which there are no pre-builts, so this is not so easy to plug and run.
The paths are hard coded so need to be updated based on your local setup. Here is the compile_commands file, which also has 3 paths to update. The compiler path, and the file path at the end twice.
[
{"directory":"F:/git/minRepro/","command":"\"C:/Program Files (x86)/compilers/clang.exe\" -Wall -isystem -g -std=c++14 -Wno-format -Wno-unneeded-internal-declaration -Werror F:/git/minRepro/exampleSource.cpp","file":"F:/git/minRepro/exampleSource.cpp"}
]
Code:
#pragma comment(lib,"Version.lib")
#include <clang/Tooling/JSONCompilationDatabase.h>
#include <clang/Tooling/Tooling.h>
#include <clang/Frontend/FrontendAction.h>
#include <clang/Sema/SemaConsumer.h>
#include <clang/AST/Type.h>
#include <clang/AST/TemplateName.h>
#include <clang/AST/Decl.h>
#include <clang/AST/DeclTemplate.h>
#include <clang/Frontend/CompilerInstance.h>
#include <iostream>
#include <cstdlib>
#include <cassert>
#include <vector>
#include <string>
class AstWalker : public clang::SemaConsumer
{
public:
AstWalker(clang::ASTContext& context)
: m_context(context)
{}
virtual void HandleTranslationUnit(clang::ASTContext& context)
{
using namespace clang;
for (auto declaration : context.getTranslationUnitDecl()->decls())
{
const auto&sm = m_context.getSourceManager();
if (!declaration->getBeginLoc().isValid())
continue;
// Only walk declarations from our file.
if (!sm.isInMainFile(sm.getSpellingLoc(declaration->getBeginLoc())))
continue;
// Find functions, and inspect their return type.
auto nodeKind = declaration->getKind();
if (nodeKind == Decl::Function)
{
auto funcDecl = cast<FunctionDecl>(declaration);
// Check for and ignore built-in functions.
if (funcDecl->getBuiltinID() != 0)
break;
walkType(funcDecl->getReturnType());
break;
}
}
}
void walkType(const clang::QualType& qualType)
{
using namespace clang;
auto classType = qualType->getTypeClass();
switch (classType)
{
case Type::Typedef:
{
auto typedefType = qualType->getAs<TypedefType>();
walkType(typedefType->desugar());
return;
}
case Type::TemplateSpecialization:
{
auto templateSpecialization = qualType->getAs<TemplateSpecializationType>();
if (templateSpecialization->isTypeAlias())
{
walkType(templateSpecialization->getAliasedType());
return;
}
std::string templateType = templateSpecialization->getTemplateName().getAsTemplateDecl()->getQualifiedNameAsString();
std::cout << templateType << "<";
auto numArgs = templateSpecialization->getNumArgs();
for (unsigned int i = 0; i < numArgs; ++i)
{
if (i > 0)
std::cout << ", ";
const clang::TemplateArgument& templateArg = templateSpecialization->getArg(i);
if (templateArg.getKind() == clang::TemplateArgument::ArgKind::Type)
{
walkType(templateArg.getAsType());
}
}
std::cout << ">";
return;
}
case Type::Record:
{
const auto record = qualType->getAs<RecordType>();
std::string recordQualifiedName = record->getAsRecordDecl()->getQualifiedNameAsString();
std::cout << recordQualifiedName;
return;
}
case Type::Elaborated:
{
auto elaboratedType = qualType->getAs<ElaboratedType>();
walkType(elaboratedType->desugar());
return;
}
case Type::SubstTemplateTypeParm:
{
auto substTemplateType = qualType->getAs<SubstTemplateTypeParmType>();
walkType(substTemplateType->desugar());
//Also tried getReplacementType.
//walkType(substTemplateType->getReplacementType());
return;
}
}
}
private:
clang::ASTContext& m_context;
};
class ExampleAction : public clang::ASTFrontendAction
{
public:
ExampleAction() {}
virtual std::unique_ptr<clang::ASTConsumer> CreateASTConsumer(clang::CompilerInstance& compiler, llvm::StringRef inFile)
{
return std::unique_ptr<clang::ASTConsumer>(new AstWalker(compiler.getASTContext()));
}
};
int main(int argc, char **argv)
{
// Create the compilation database.
std::string errorOut;
std::unique_ptr<clang::tooling::JSONCompilationDatabase> compilationDatabase = clang::tooling::JSONCompilationDatabase::loadFromFile("F:/git/minRepro/compile_commands.json", errorOut, clang::tooling::JSONCommandLineSyntax::AutoDetect);
if (compilationDatabase == nullptr || !errorOut.empty())
{
std::cout << "[Error] Failed to load compilation database. Error=" << errorOut.c_str() << std::endl;
return false;
}
std::vector<std::string> headerFiles;
headerFiles.push_back("F:/git/minRepro/exampleSource.cpp");
clang::tooling::ClangTool tool(*compilationDatabase, llvm::ArrayRef<std::string>(headerFiles));
auto toolResult = tool.run(clang::tooling::newFrontendActionFactory<ExampleAction>().get());
if (toolResult == 1)
{
std::cout << "[Error] Error occurred. Check log. Aborting.\n";
assert(false);
return false;
}
}
In some cases, the template information is nested within ClassTemplateSpecializationDecl for a given RecordType. You can read it by casting your RecordDecl into a ClassTemplateSpecializationDecl.
const auto record = qualType->getAs<clang::RecordType>();
auto classTemplateSpecialization = llvm::dyn_cast<clang::ClassTemplateSpecializationDecl>(record->getAsRecordDecl());
if (classTemplateSpecialization)
{
const auto& args = classTemplateSpecialization->getTemplateArgs();
for (unsigned int i = 0; i < args.size(); ++i)
{
const clang::TemplateArgument& templateArg = args[i];
// Read arg info as needed.
}
}
else
{
// Not a template specialization.
}

compile time/runtime variable name/enum value name information

Is it possible to obtain variable name or enum value name at compile time/runtime? In particular, namespace::/class::/struct::/union::-qualified (with adjustable depth, like have UNIX patch unility -p/--strip= option). In gcc 4.8.1 I can write:
#include <iostream>
#include <ostream>
#include <cstdlib>
enum class mnemocode
{
fwait,
finit,
fninit,
fstsw,
fnstsw,
// ...
sahf,
ret,
prologue,
epilogue,
sp_inc,
sp_dec,
call
};
inline /* till C++14 cannot be constexpr */
auto mnemocode_name(mnemocode _mnemocode)
{
switch (_mnemocode) {
case mnemocode::fwait : return "fwait";
case mnemocode::finit : return "finit";
case mnemocode::fninit : return "fninit";
case mnemocode::fstsw : return "fstsw";
case mnemocode::fnstsw : return "fnstsw";
// ...
case mnemocode::sahf : return "sahf";
case mnemocode::ret : return "ret";
case mnemocode::prologue : return "prologue";
case mnemocode::epilogue : return "epilogue";
case mnemocode::sp_inc : return "sp_inc";
case mnemocode::sp_dec : return "sp_dec";
case mnemocode::call : return "call";
default : return "[unknown instruction]";
}
}
inline
std::ostream & operator << (std::ostream & out, mnemocode _mnemocode)
{
return out << mnemocode_name(_mnemocode);
}
int main()
{
std::cout << mnemocode::fwait << std::endl; // fwait
return EXIT_SUCCESS;
}
But I want to be able to do the following:
template< typename M, typename = typename std::enable_if< std::is_same< mnemocode, typename std::remove_reference< typename std::remove_cv< M >::type >::type >::value >
inline constexpr
auto mnemocode_name(M && _mnemocode)
{
constexpr auto const depth = std::numeric_limits< std::size_t >::max(); // remove all qualifiers before last operator ::
return abi::__get_value_info(_mnemocode).name(depth); // compile time if M is constexpr
}
by means of some imaginary constexpr abi::__get_value_info(symbol) class.
GCC allows me to write:
#include <iostream>
#include <string>
#include <type_traits>
#include <cxxabi.h>
#include <cstdlib>
template< typename T >
/* cannot be constexpr :( */
const char * this_type()
{
return __PRETTY_FUNCTION__;
}
template< typename T >
std::string theirs_type()
{
int status = 0;
auto realname_(abi::__cxa_demangle(typeid(T).name(), nullptr, nullptr, &status));
switch (status) {
case -1: return "Could not allocate memory";
case -2: return "Invalid name under the C++ ABI mangling rules";
case -3: return "Invalid argument to demangle";
}
std::string os(realname_);
std::free(realname_);
if (std::is_volatile< T >::value) {
os = "volatile " + os;
}
if (std::is_const< T >::value) {
os += " const";
}
if (std::is_rvalue_reference< T >::value) {
os += " &&";
} else if (std::is_lvalue_reference< T >::value) {
os += " &";
}
return os;
}
int main()
{
std::cout << this_type< decltype(static_cast< double const >(double())) >() << std::endl; // const char* this_type() [with T = double]
std::cout << theirs_type< double const && >() << std::endl; // double &&
return EXIT_SUCCESS;
}
But this is only about the type names and too far from compile time deduction.
I guess that's all I come up here, would require almost embedded into my executable debugger and the availability of the section with the debug information. But I still do not think, that it's impossible at all.
There may be other solutions but the way I normally do this is to play with #defines
1) define your enums in a file (say enumdef.h)
ENUMDEF(fwait)
ENUMDEF(finit)
...
ENUMDEF(call)
2) Declare a header for the enums
#ifndef EnumDefEnum_h
#define EnumDefEnum_h
enum mnemcode
{
#define ENUMDEF(x) x,
#include "enumdef.h"
#undef ENUMDEF
mnemcodeMax
};
#endif
3) To print them
#include "enumdefenum.h"
static const char* mnem_str[] =
{
#define ENUMDEF(x) #x,
#include "enumdef.h"
#undef ENUMDEF
""
};
const char* mnem_name(mnemcode index)
{
return mnem_str[index];
}
4) You could even use them in switch statements if you keep to a naming convention
switch (index)
{
// Put in a pragma to tell you which one it is because the compiler will
// tell you the line number in enumdef.h: not the one in this file
#pragma message("switch statement in some routine")
#define ENUMDEF(x) \
case x: Do##x(whatever); break;
#include "enumdef.h"
#undef ENUMDEF
}