Im working on a big project and I have a lot of errno macros.
I want to write a helper functions for the logger that stringify each of these errno to a string. i decided to use x-macros but Im getting compilation errors
in the first place the code was like this:
// project_errno.h
#define PROJECT_ERR_KEY_FAILURE 12222
#define PROJECT_ERR_CIPHER_ZERO_PADDING 12345
#define PROJECT_ERR_FAILED_TO_SETUP_ENC_KEY 14004
the way i sort it out is as the following:
In a different file i places the x-macros:
// project_errno.hx
PROJECT_ERR_FUNC(PROJECT_ERR_KEY_FAILURE) 12222
PROJECT_ERR_FUNC(PROJECT_ERR_CIPHER_ZERO_PADDING) 12345
PROJECT_ERR_FUNC(PROJECT_ERR_FAILED_TO_SETUP_ENC_KEY) 14004
then I turned it into an enum:
// project_errno.h
enum {
#define PROJECT_ERR_FUNC(name, value) name=value,
#include "project_errno.hx"
#undef PROJECT_ERR_FUNC
};
then i added a function that will be used by the logger:
// logging.h (declaration) and (definition) logging.c
const char* stringify_errno(int errno) {
switch (errno) {
#define PROJECT_ERR_FUNC(name, value) case name: return #value ;
#include "project_errno.hx"
#undef PROJECT_ERR_FUNC
}
}
So, looks pretty good, but i can't get it to compile, Im getting the following compilation errros:
project_errno.h:8:53: error: error: expected identifier before numeric constant
#define PROJECT_ERR_CIPHER_ZERO_PADDING 12345
^
..../project_errno.h:17:30: note: in definition of macro ‘PROJECT_ERR_FUNC’
#define PROJECT_ERR_FUNC(name, value) name=value,
^~~~
..../project_errno.hx:47:14: note: in expansion of macro ‘PROJECT_ERR_CIPHER_ZERO_PADDING ’PROJECT_ERR_FUNC(PROJECT_ERR_CIPHER_ZERO_PADDING, 12345)
project_errno.h:8:53: error: error: expected ‘}’ before numeric constant
#define PROJECT_ERR_CIPHER_ZERO_PADDING 12345
^
..../project_errno.h:17:30: note: in definition of macro ‘PROJECT_ERR_FUNC’
#define PROJECT_ERR_FUNC(name, value) name=value,
^~~~
..../project_errno.hx:47:14: note: in expansion of macro ‘PROJECT_ERR_CIPHER_ZERO_PADDING ’PROJECT_ERR_FUNC(PROJECT_ERR_CIPHER_ZERO_PADDING, 12345)
project_errno.h:8:53: error: expected unqualified-id before numeric constant
#define PROJECT_ERR_CIPHER_ZERO_PADDING 12345
^
..../project_errno.h:17:30: note: in definition of macro ‘PROJECT_ERR_FUNC’
#define PROJECT_ERR_FUNC(name, value) name=value,
^~~~
..../project_errno.hx:47:14: note: in expansion of macro ‘PROJECT_ERR_CIPHER_ZERO_PADDING ’PROJECT_ERR_FUNC(PROJECT_ERR_CIPHER_ZERO_PADDING, 12345)
^~~~~~~~~~~~~~~~~~~~~~~
In file included from ......../project_errno.h:20:1: error: expected declaration before ‘}’ token
};
^
..../project_errno.h:17:30: note: in definition of macro ‘PROJECT_ERR_FUNC’
#define PROJECT_ERR_FUNC(name, value) name=value,
^~~~
..../project_errno.hx:47:14: note: in expansion of macro ‘PROJECT_ERR_CIPHER_ZERO_PADDING ’PROJECT_ERR_FUNC(PROJECT_ERR_CIPHER_ZERO_PADDING, 12345)
^~~~~~~~~~~~~~~~~~~~~~~
I can't understand why im getting those errors (im getting the same error message multiple time in the same compilation session), and i hope you guys could help me.
Also, if you have any other solution to solve the problem i intended to solve in the first place (using the errno macros and add a functions to stringify those errnos whenever Im adding an errno to the project [in only one place]), i'd love to hear about it Thanks
I'd follow the recipe shown in the Wikipedia page about X Macros:
Implementation
An X macro application consists of two parts:
The definition of the list's elements.
Expansion(s) of the list to generate fragments of declarations or statements.
The list is defined by a macro or header file (named, LIST) which generates no code by itself, but merely consists of a sequence of invocations of a macro (classically named X) with the elements' data. Each expansion of LIST is preceded by a definition of X with the syntax for a list element. The invocation of LIST expands X for each element in the list.
In particular the second example, the one with X macro as argument
Pass name of the worker macro into the list macro. This both avoids defining an obscurely named macro (X), and alleviates the need to undefine it.
Which, in OP's use case, leads to the following three files:
// project_errno.h
#ifndef PROJECT_ERRNO_H
#define PROJECT_ERRNO_H
#define FOR_EACH_ERR_ID_VALUE_PAIR(DO) \
DO(PROJECT_ERR_KEY_FAILURE, 12222) \
DO(PROJECT_ERR_CIPHER_ZERO_PADDING, 12345) \
DO(PROJECT_ERR_FAILED_TO_SETUP_ENC_KEY, 14004)
#define DEFINE_ENUM_ITEM(err, value) err = value,
enum project_errs {
FOR_EACH_ERR_ID_VALUE_PAIR( DEFINE_ENUM_ITEM )
};
#undef DEFINE_ENUM_ITEM
#endif
// logging.h
#ifndef LOGGING_H
#define LOGGING_H
const char* stringify_errno(int errno);
#endif
// logging.c
#include <stdio.h>
#include "project_errno.h"
#define STRINGIFY_ERR_VALUE_NAME(name, value) case name: \
return "[" #value "] " #name;
const char* stringify_errno(int errno)
{
switch (errno) {
FOR_EACH_ERR_ID_VALUE_PAIR(STRINGIFY_ERR_VALUE_NAME)
default:
return "[-----] UNKWOWN";
}
}
#undef STRINGIFY_ERR_VALUE_NAME
Testable here: https://wandbox.org/permlink/aNJCI7lQihkFnYzp
Edited answer:
I was able to generate the enum using
enum {
#define PROJECT_ERR_FUNC(name) , name =
PROJECT_ERR_START_OF_ENUM = -1
#include "project_errno.hx"
#undef PROJECT_ERR_FUNC
};
but had to remove the backtick at the end of project_errno.hx. The above code will only work if you do not include project_errno.h before.
A complete minimum working program would be
enum {
#define PROJECT_ERR_FUNC(name) , name =
PROJECT_ERR_START_OF_ENUM = -1
#include "project_errno.hx"
#undef PROJECT_ERR_FUNC
};
#include <iostream>
int main() {
std::cout << PROJECT_ERR_KEY_FAILURE << std::endl;
return 0;
}
that will print 12222.
Related
I have a macro which is supposed to take a function name and a list of function parameters as arguments and generate a function from that:
#include <iostream>
#include <string>
#include <array>
#define COMMA ,
#define DEFINE_FUNC(funcName,parameters,impl) \
void funcName(parameters) impl
DEFINE_FUNC(
test_func, /* function name */
float f COMMA float f2, /* Parameters */
{
std::cout<<f<<","<<f2<<std::endl;
} /* Implementation */
)
int main()
{
test_func(1.f,2.f);
}
This compiles fine on both gcc and Visual Studio. The problem is, I now have to define an intermediary macro that relies on DEFINE_FUNC, and then use that macro for defining the function instead:
#include <iostream>
#include <string>
#include <array>
#define COMMA ,
#define DEFINE_FUNC(funcName,parameters,impl) \
void funcName(parameters) impl
#define DEFINE_FUNC2(funcName,parameters,impl) \
DEFINE_FUNC(funcName,parameters,impl)
DEFINE_FUNC2(
test_func, /* function name */
float f COMMA float f2, /* Parameters */
{
std::cout<<f<<","<<f2<<std::endl;
} /* Implementation */
)
int main()
{
test_func(1.f,2.f);
}
On Visual Studio this still compiles fine, but gcc refuses to compile it because the f and f2 parameters are now interpreted as separate arguments by DEFINE_FUNC2 because of the COMMA:
main.cpp:15:5: error: too many arguments provided to function-like macro invocation
{
^
main.cpp:6:9: note: macro 'DEFINE_FUNC' defined here
#define DEFINE_FUNC(funcName,parameters,impl) \
^
main.cpp:12:1: error: unknown type name 'DEFINE_FUNC'
DEFINE_FUNC2(
^
main.cpp:10:5: note: expanded from macro 'DEFINE_FUNC2'
DEFINE_FUNC(funcName,parameters,impl)
^
I could just copy the contents of DEFINE_FUNC into DEFINE_FUNC2, but this is a very simplified example and it would cause significant redundancy in the actual code.
I tried changing float f COMMA float f2 to (float f,float f2) to force the preprocessor to ignore the comma, but that also fails to compile:
#define COMMA ,
#define DEFINE_FUNC(funcName,parameters,impl) \
void funcName##parameters impl
#define DEFINE_FUNC2(funcName,parameters,impl) \
DEFINE_FUNC(funcName,parameters,impl)
DEFINE_FUNC2(
test_func, /* function name */
(float f , float f2), /* Parameters */
{
std::cout<<f<<","<<f2<<std::endl;
} /* Implementation */
)
Error:
main.cpp:12:1: error: pasting formed 'test_func(', an invalid preprocessing token
DEFINE_FUNC2(
^
Is there some way to make this work with both gcc and Visual Studio?
Your last solution is mostly valid, but in a function definition, the function name and parameters do not form a single token, so you should not join them using ##:
#define DEFINE_FUNC(funcName,parameters,impl) \
void funcName parameters impl
There is a macro that I want to use that takes arguments in this format:
COMB(unique identifier, argument 1, argument 2, argument 3)
I want to create a wrapper for it that automatically generates the unique identifier. I found this answer that has code to generate a random ID, which I am using in my code:
#define PP_CAT(a, b) PP_CAT_I(a, b)
#define PP_CAT_I(a, b) PP_CAT_II(~, a ## b)
#define PP_CAT_II(p, res) res
#define UNIQUE_NAME(base) PP_CAT(base, __COUNTER__)
#define COMB_U(key, ...) COMB(UNIQUE_NAME(comb_r_), key, __VA_ARGS__)
// Using the macro
// The parameters are enums
COMB_U(KB_1, KB_2, KC_Y)
When I compile this I get a bunch of errors from inside of the COMB macro code like this:
‘cmb_UNIQUE_NAME’ declared as function returning an array
function ‘cmb_UNIQUE_NAME’ is initialized like a variable
As well as inside of the file with my code:
‘comb_r_’ undeclared here (not in a function)
#define COMB_U(key, ...) COMB(UNIQUE_NAME(comb_r_), key, VA_ARGS)
And a bunch of messages that don't seem to be associated with an error, like this one:
note: in expansion of macro ‘COMB_U’
COMB_U(KB_1, KB_2, KC_Y)
How can I fix this?
For reference, the source for the COMB macro is here: https://github.com/germ/qmk_firmware/blob/master/keyboards/gboards/g/keymap_combo.h
I wrote this simple program
#include <time.h>
int main()
{
#if ((clock_t)1000)
int x = 10;
#endif
return 0;
}
On compilation, I see the following error:
Error C1012 unmatched parenthesis: missing ')'
Why am I getting this error?
Changing the line from:
#if ((clock_t)1000)
to:
#if (clock_t)1000
resolves the compilation error.
But I can't do that, since ((clock_t)1000) is defined as a macro using the #define directive in the limits.h header file as :
#define CLOCKS_PER_SEC ((clock_t)1000)
and I need to use that directly.
EDIT:
Please pardon me for framing the question in such an unclear way.
Reframing my question now:
I have the following code:
#include <time.h>
#define DUMMY_CLOCKS_PER_SEC ((clock_t)1000)
int main()
{
#if CLOCKS_PER_SEC != DUMMY_CLOCKS_PER_SEC
#error "out of sync"
#endif
return 0;
}
But this gives the compilation error:
Error C1012 unmatched parenthesis: missing ')'
The preprocessor doesn't know anything about C++ datatypes, and doesn't understand cast expressions. It's used for simple text processing, and == and != can only compare single tokens.
Do the comparison in C++, not the preprocessor.
static_assert(CLOCKS_PER_SEC == DUMMY_CLOCKS_PER_SEC, "out of sync");
int main() {
return 0;
}
Don't worry about the runtime performance overhead. Since both macros expand to literals, the compiler will optimize it away.
You are confusing a preprocessor macro definition (CLOCKS_PER_SEC) with its expansion (that is implementation defined, and in your case seems to be ((clock_t)1000)).
It's not very clear what you want to do in your code.
If you want to check if this macro is defined, you can use the preprocessor #ifdef, e.g.:
#ifdef CLOCKS_PER_SEC
// your code
#endif
Anyway, this CLOCKS_PER_SEC macro is defined by the standard, so it should be always defined in a standard-compliant time.h library implementation.
If you have something different in your mind, please clarify your goal.
EDIT Based on your clarifying comment below, you may want to use an if to compare the values (expansions) of these two macros:
if (DUMMY_CLOCKS_PER_SEC != CLOCKS_PER_SEC) {
...
} else {
...
}
((clock_t)1000) is defined as a macro using the #define directive in the limits.h header file as :
#define CLOCKS_PER_SEC ((clock_t)1000)
The file does not define a macro named ((clock_t)1000). It defines a macro named CLOCKS_PER_SEC. ((clock_t)1000) is the value of the macro.
((clock_t)1000) is not a macro and is something that cannot be used in an #if directive.
Thanks for all the responses everyone.
Another solution I figured out for this problem is to use constexpr specifier which is a feature of c++11. ConstExpr allows us to evaluate the value of a variable or a function at compile time.
Changing the code from:
#if CLOCKS_PER_SEC != DUMMY_CLOCKS_PER_SEC
#error "out of sync"
#endif
to the following resolves the issue:
constexpr int DUMMY_CLOCK_NOT_EQUAL = (DUMMY_CLOCKS_PER_SEC != CLOCKS_PER_SEC) ? 1 : 0;
#if DUMMY_CLOCK_NOT_EQUAL
#error "out of sync"
#endif
I'm not sure if that's the right terminology to use, however my problem is that the a macro call ("PLUGIN_NAME") as a parameter to another macro call ("IMPLEMENT_MODULE"), which in turn prints it as a string, prints that argument as the macro call ("somePLUGIN_NAME") rather than the expanded result ("someSomePluginName").
Note that "IMPLEMENT_MODULE" is an API call so I can't change that.
#define IMPLEMENT_MODULE(name) something##name
#define PLUGIN_NAME SomePluginName
#define _STR(s) #s
#define STR(s) _STR(s)
#define PLUGIN_NAME_STR STR(PLUGIN_NAME)
int main()
{
string expected = "somethingSomePluginName";
string actual = STR(IMPLEMENT_MODULE(PLUGIN_NAME));
printf("expected: %s || actual: %s\n", expected.c_str(), actual.c_str());
assert(expected == actual);
}
I've put it here:
http://codepad.org/FRzChJtD
You need another helper macro to concatenate the preprocessor tokens after macro-expanding them:
#define IMPLEMENT_MODULE_2(A, B) A##B
#define IMPLEMENT_MODULE(name) IMPLEMENT_MODULE_2(something, name)
See working example here
This technical explanation is that macro expansion will not occur if the token-pasting (##) or stringizing operator (#) are found by the preprocessor.
Shouldn't this:
#define MOGSH_CONCAT (x,y) x##y
#define MOGSH_DOUBLE (a,b) MOGSH_CONCAT(a,b)
#define MOGSH_DEFINEPROC (p) MOGSH_DOUBLE(_gt,p) options_dialog::p;
MOGSH_DEFINEPROC(AssignMainForm);
happily expand to:
_gtAssignMainForm options_dialog::AssignMainForm;
Given that _gt is not defined, _gtAssignMainForm is:
typedef void (__stdcall *_gtAssignMainForm)();
and options_dialog is just a class where AssignMainForm is a static member.
Instead, in MSVC9, I get the error:
'a' : undeclared identifier
on the line containing
MOGSH_DEFINEPROC(AssignMainForm);
In the definition of a function-like macro there can be no whitespace between the macro name and the ( beginning the parameter list.
#define MOGSH_CONCAT(x,y) x##y
// ^ no whitespace allowed here
As you have it now (with whitespace), MOGSH_CONCAT is an object-like macro with a replacement list of (x,y) x##y, which is why you are getting such strange results.