Have compiler check the number of array initializers - c++
Initializing an array (in C++, but any solution which works for C will likely work here as well) with less initializers than it has elements is perfectly legal:
int array[10] = { 1, 2, 3 };
However, this can be a source of obscure bugs. Is there a way to have the compiler (gcc) check the number of initializers for one specific array, and emit a warning or even an error if declared and actual size don't match?
I know I can use int array[] = { 1, 2, 3 }; and could then use static assertions involving sizeof(array) to verify my expectation there. But I'm using array in other translation units, so I have to declare it with an explicit size. So this trick won't work for me.
(promoted from a comment as requested)
If the values in the array are important to the correct functionality of the system, and having zero-initialized values at the end causes bugs, then I would just add a unit test to verify the array contains the right data, instead of trying to enforce it in the code.
Since you are using array in other translation units, it apparently has external linkage. In this case, you are allowed to declare it twice, as long as the declarations give it the same type. So simply declare it twice, once allowing the compiler to count the initializers and once specifying the size. Put this line in one source file, before any header that declares array:
int array[] = { 1, 2, 3 };
Later in the same file, put an #include line that declares array, with a line such as:
extern int array[10];
If the array sizes differ in the two declarations, the compiler will report an error. If they are the same, the compiler will accept them.
I have an idea.
#define C_ASSERT(expr) extern char CAssertExtern[(expr)?1:-1]
#define NUM_ARGS__(X, \
N64,N63,N62,N61,N60, \
N59,N58,N57,N56,N55,N54,N53,N52,N51,N50, \
N49,N48,N47,N46,N45,N44,N43,N42,N41,N40, \
N39,N38,N37,N36,N35,N34,N33,N32,N31,N30, \
N29,N28,N27,N26,N25,N24,N23,N22,N21,N20, \
N19,N18,N17,N16,N15,N14,N13,N12,N11,N10, \
N09,N08,N07,N06,N05,N04,N03,N02,N01, N, ...) N
#define NUM_ARGS(...) \
NUM_ARGS__(0, __VA_ARGS__, \
64,63,62,61,60, \
59,58,57,56,55,54,53,52,51,50, \
49,48,47,46,45,44,43,42,41,40, \
39,38,37,36,35,34,33,32,31,30, \
29,28,27,26,25,24,23,22,21,20, \
19,18,17,16,15,14,13,12,11,10, \
9, 8, 7, 6, 5, 4, 3, 2, 1, 0)
#define DECL_INIT_ARRAYN(TYPE, NAME, COUNT, N, ...) \
C_ASSERT(COUNT == N); \
TYPE NAME[COUNT] = { __VA_ARGS__ }
#define DECL_INIT_ARRAY(TYPE, NAME, COUNT, ...) \
DECL_INIT_ARRAYN(TYPE, NAME, COUNT, NUM_ARGS(__VA_ARGS__), __VA_ARGS__)
DECL_INIT_ARRAY(const int, array3_3, 3, 1, 2, 3);
int main(void)
{
DECL_INIT_ARRAY(const int, array5_4, 5, 1, 2, 3, 4);
DECL_INIT_ARRAY(const int, array5_6, 5, 1, 2, 3, 4, 5, 6);
return 0;
}
Output (ideone):
prog.c: In function ‘main’:
prog.c:33:3: error: size of array ‘CAssertExtern’ is negative
prog.c:34:3: error: size of array ‘CAssertExtern’ is negative
prog.c:34:3: error: excess elements in array initializer [-Werror]
prog.c:34:3: error: (near initialization for ‘array5_6’) [-Werror]
prog.c:34:3: error: unused variable ‘array5_6’ [-Werror=unused-variable]
prog.c:33:3: error: unused variable ‘array5_4’ [-Werror=unused-variable]
prog.c:34:3: error: unused variable ‘CAssertExtern’ [-Werror=unused-variable]
cc1: all warnings being treated as errors
UPD: The OP has found a shorter C++11 solution, building upon the same idea of using __VA_ARGS__ and a static/compile-time assertion:
#include <tuple>
#define DECL_INIT_ARRAY(TYPE, NAME, COUNT, ...) \
static_assert(COUNT == \
std::tuple_size<decltype(std::make_tuple(__VA_ARGS__))>::value, \
"Array " #NAME " should have exactly " #COUNT " initializers"); \
TYPE NAME[COUNT] = { __VA_ARGS__ }
DECL_INIT_ARRAY(const int, array3_3, 3, 1, 2, 3);
int main(void)
{
DECL_INIT_ARRAY(const int, array5_4, 5, 1, 2, 3, 4);
DECL_INIT_ARRAY(const int, array5_6, 5, 1, 2, 3, 4, 5, 6);
return 0;
}
Output (ideone):
prog.cpp: In function ‘int main()’:
prog.cpp:13:3: error: static assertion failed: Array array5_4 should have exactly 5 initializers
prog.cpp:14:3: error: static assertion failed: Array array5_6 should have exactly 5 initializers
prog.cpp:14:3: error: too many initializers for ‘const int [5]’
prog.cpp:13:3: warning: unused variable ‘array5_4’ [-Wunused-variable]
prog.cpp:14:3: warning: unused variable ‘array5_6’ [-Wunused-variable]
I looked around for a specific answer to this in C99 and found an answer here: How can I use “sizeof” in a preprocessor macro?
If you don't define the size of your array and use:
int test[] = {1,2}
STATIC_ASSERT(sizeof(test)/sizeof(test[0]) == 3)
/* with C11 support: */
_Static_assert(sizeof(test)/sizeof(test[0]) == 3)
/* or more easily */
ASSERT_ARRAY_LENGTH(test, 3);
you can easily detect if the sizeof the array is as you expected. A compilation error will be raised if the static assert fails. There is no run time overhead. A very solid implementation of the static assert can be found here:
Static assert implementation C
for your convenience:
#define ASSERT_CONCAT_(a, b) a##b
#define ASSERT_CONCAT(a, b) ASSERT_CONCAT_(a, b)
/* These can't be used after statements in c89. */
#ifdef __COUNTER__
#define STATIC_ASSERT(e,m) \
;enum { ASSERT_CONCAT(static_assert_, __COUNTER__) = 1/(int)(!!(e)) }
#else
/* This can't be used twice on the same line so ensure if using in headers
* that the headers are not included twice (by wrapping in #ifndef...#endif)
* Note it doesn't cause an issue when used on same line of separate modules
* compiled with gcc -combine -fwhole-program. */
#define STATIC_ASSERT(e,m) \
;enum { ASSERT_CONCAT(assert_line_, __LINE__) = 1/(int)(!! (e)) }
#endif
I Added a macro on top of this one specifically for validating the sizeof an array. The number of elements must exactly match the specified length:
#define ASSERT_ARRAY_LENGTH(array, length)\
STATIC_ASSERT(sizeof(array)/sizeof(array[0]) == length,\
"Array is not of expected length")
If you don't need to support C99 you can use the new C11 feature _Static_assert. More info here.
If you don't require C support, you can also rely on the c++ static assert:
std::size(test) == 3; /* C++ 17 */
(std::end(test) - std::begin(end)) == 3; /* C++ 14 */
Related
Why does GCC claim I'm violating "at least one argument for variadic macro" when I have at least one argument?
Here is the example code I used to reproduce this error: #include <iostream> #define TEST_2_ARG_MACRO_OVERLOAD(_1,_2,FUNC_NAME,...) FUNC_NAME #define TEST_HELLO_IMPL(condition) do{ \ if(!(condition)) {\ std::cout << "hello!" << std::endl; \ }\ } while(0) #define TEST_HELLO_MESSAGE_IMPL(condition, message) do{ \ if(!(condition)) {\ std::cout << "hello" << message << std::endl; \ }\ } while(0) //Runs during runtime and debug #define TEST_HELLO(...) TEST_2_ARG_MACRO_OVERLOAD(__VA_ARGS__, TEST_HELLO_MESSAGE_IMPL, TEST_HELLO_IMPL)(__VA_ARGS__) int main() { auto x = 3 * (3); TEST_HELLO(x >= 3); } In godbolt with GCC x86-64 8.2 using: -std=c++14 -Wall -Wextra -Wshadow -Wnon-virtual-dtor -Wpedantic -Werror as arguments I get the following error: <source>:24:22: error: ISO C++11 requires at least one argument for the "..." in a variadic macro [-Werror] TEST_HELLO(x >= 3); ^ cc1plus: all warnings being treated as errors Compiler returned: 1 Yet there is clearly at least one argument in the macro. Now I know this compiles when I don't enable this warning, and when I don't enable this warning as an error, but for other reasons I want to keep these compiler flags (or at least keep the same results). Why does GCC claim that I've passed zero arguments when I have not?
The problem is actually with TEST_2_ARG_MACRO_OVERLOAD, the error message is slightly misleading. The macro takes 3 arguments and ... , in ISO C++ that means you must pass at least 4 arguments but in fact you only pass 3. (Ref: C++17 [cpp.replace]/4) The TEST_HELLO expands to TEST_2_ARG_MACRO_OVERLOAD(x >= 3, TEST_HELLO_MESSAGE_IMPL, TEST_HELLO_IMPL)(x >= 3) .
Why gcc breaks in recursively expanding macros
Good day, I've faced with a strange problem while compiling very simple C++ program which leverages recursive macros expansion: #define FINAL(a1, a2, a3) const char *p = "final values are: " #a1 " " #a2 " " #a3; #define SPLIT(a1, a2) a1, a2 #define BRACES(a1, a2) ( a1, a2 ) #define START(macro, a1, a2) macro BRACES(a1, SPLIT a2) START(FINAL, 1, (2, 3)) int main(int argc, char* argv[]) { std::cout << p << std::endl; return 0; } The program is expecting to print "final values are: 1 2 3" text. And it does on a Visual Studio 2008. But I see an issue when trying to compile it with mingw32 gcc-6.3 on Windows 7 and with gcc-5.4 on Linux Ubuntu-16: $ g++ -I /e/dev-libs/boost/boost_1_60_0-mingw32/include/boost-1_60 test.cpp test.cpp:7:24: error: expected constructor, destructor, or type conversion before '(' token #define BRACES(a1, a2) ( a1, a2 ) ^ test.cpp:8:36: note: in expansion of macro 'BRACES' #define START(macro, a1, a2) macro BRACES(a1, SPLIT a2) ^~~~~~ test.cpp:12:1: note: in expansion of macro 'START' START(FINAL, 1, (2, 3)) ^~~~~ It looks like it doesn't depend on a C++ standard, I've tried -std=c++11 and -std=c++03 with gcc. I've reread part 16.3 "Macro replacement" of C++ 11 standard a few times, but I guess I've missed something important there. What can be wrong here with the code? One more important thing: BOOST_PP_SEQ_FOR_EACH_I_R from boost preprocessor library can't be compiled too, which is more than strange: #include <iostream> #include <boost/preprocessor/seq/for_each_i.hpp> #define FINAL2(r, data, id, value) const char *p ## id = #value; BOOST_PP_SEQ_FOR_EACH_I_R(_, FINAL2, _, (a)(b)(c)) int main() { std::cout << p1 << std::endl; return 0; } Error output: $ g++ -I /e/dev-libs/boost/boost_1_60_0-mingw32/include/boost-1_60 -std=c++03 test2.cpp In file included from test2.cpp:2:0: E:/dev-libs/boost/boost_1_60_0-mingw32/include/boost-1_60/boost/preprocessor/seq/for_each_i.hpp:96:96: error: expected constructor, destructor, or type conversion before '(' token # define BOOST_PP_SEQ_FOR_EACH_I_R_DETAIL_CHECK_EXEC(r, macro, data, seq) BOOST_PP_FOR_ ## r((macro, data, seq, 0, BOOST_PP_SEQ_SIZE(seq)), BOOST_PP_SEQ_FOR_EACH_I_P, BOOST_PP_SEQ_FOR_EACH_I_O, BOOST_PP_SEQ_FOR_EACH_I_M) ^ E:/dev-libs/boost/boost_1_60_0-mingw32/include/boost-1_60/boost/preprocessor/control/iif.hpp:32:31: note: in expansion of macro 'BOOST_PP_SEQ_FOR_EACH_I_R_DETAIL_CHECK_EXEC' # define BOOST_PP_IIF_1(t, f) t ^ E:/dev-libs/boost/boost_1_60_0-mingw32/include/boost-1_60/boost/preprocessor/control/iif.hpp:25:39: note: in expansion of macro 'BOOST_PP_IIF_1' # define BOOST_PP_IIF_I(bit, t, f) BOOST_PP_IIF_ ## bit(t, f) ^~~~~~~~~~~~~
The problem with first example seems to be rather straightforward. After first expansion preprocessing will stop doing what you want it to do: START(FINAL, 1, (2, 3)) // becomes FINAL BRACES(1, SPLIT (2, 3)) There are no arguments for FINAL so generated code will be messed up.
As #VTT says, the replacement for START(FINAL, 1, (2, 3)) is FINAL BRACES(1, SPLIT(2, 3)); that text is then rescanned, but it does not have the form of a macro expansion of FINAL. You can achieve the effect you want with another level of indirection (or macro expansion, to be more precise): #define SPLIT(a1, a2) a1, a2 #define BRACES(a1, a2) ( a1, a2 ) #define APPLY(a1, a2) a1 a2 #define START(macro, a1, a2) APPLY(macro, BRACES(a1, SPLIT a2))
Using the rules specified by the standard, your macro: START(FINAL, 1, (2, 3)) ...expands during argument substitution to: FINAL BRACES(1, SPLIT (2, 3)) This is then rescanned during rescan and further replacement; during this step, FINAL is seen, but it doesn't have arguments after it. Since you only have a function-like macro, nothing happens. So the CPP moves along. BRACES however has two arguments, and is a two argument function-like macro, so it expands (SPLIT's expansion is part of this expansion), leaving you with this: FINAL (1, 2, 3) ...and now the CPP is done with BRACES, so it moves along further. There's no step to back up and evaluate/expand FINAL. If you want that to happen, you need an indirect macro (such as the one #rici showed you). That Microsoft's CPP expands this isn't surprising; MS's CPP is non-standard. As for the second question (really should be a separate question though): BOOST_PP_SEQ_FOR_EACH_I_R(_, FINAL2, _, (a)(b)(c)) That call is wrong. The _R variant macros expect an r argument that corresponds to macro sets (this is the first argument). These are numbers; they can't just be _. 1 would work; however, you don't actually need to call the _R version unless you're doing something recursive/complex.
How do I generate an arbitrarily long list of parameters using macros in C++?
I want a macro that takes any even number of parameters, stitches each pair together, adds some name to the end, let's say Type, and then passes them as template arguments to some type, let's call it CompileTimeList. So I would give it something like this: MACRO( \ First,Set, \ Second,Type, \ Third,Thing) and it would expand to: CompileTimeList< FirstSetType, SecondTypeType, ThirdThingType> The naive solution would be a variadic recursive template, which is not allowed: Can we have recursive macros? I could manually hack together a macro using this methodology: Macro recursive expansion to a sequence But it would be limited to however many parameters I've defined. I want to be able to expand any length of parameters. Is this possible in C++11? I also have Boost 1.55 available, which wasn't mentioned in the other questions. In order to attempt to implement chris' answer, I've written the following program: 1 #include <boost/preprocessor.hpp> 2 3 #include <vector> 4 #include <algorithm> 5 6 #define MACRO(...) \ 7 std::find( \ 8 BOOST_PP_ENUM( \ 9 BOOST_PP_DIV(BOOST_PP_VARIADIC_SIZE(__VA_ARGS__), 2), \ 10 ENUM_MACRO, \ 11 BOOST_PP_VARIADIC_TO_TUPLE(__VA_ARGS__) \ 12 ) \ 13 ) 14 15 #define ENUM_MACRO(z, n, data) \ 16 BOOST_PP_CAT( \ 17 BOOST_PP_CAT( \ 18 BOOST_PP_TUPLE_ELEM(BOOST_PP_MUL(n, 2), data), \ 19 BOOST_PP_TUPLE_ELEM(BOOST_PP_INC(BOOST_PP_MUL(n, 2)), data) \ 20 ), \ 21 Type \ 22 ) 23 24 25 typedef int FirstNameType; 26 typedef std::vector<int>::iterator SecondNameType; 27 typedef std::vector<int>::iterator ThirdNameType; 28 29 int main() 30 { 31 std::vector<int>(); 32 auto it = MACRO(First, Name, Second, Name, Third, Name); 33 return 0; 34 } Which produces an avalanche of errors relating to the use of commas, starting with: test.cpp: In function ‘int main()’: src/boost/boost/preprocessor/punctuation/comma.hpp:19:27: error: expected primary-expression before ‘,’ token # define BOOST_PP_COMMA() ,
Your desired syntax can be achieved through Boost with a couple macros: #define BOOST_PP_VARIADICS #include <boost/preprocessor.hpp> //The main part is BOOST_PP_ENUM, which loops the number of arguments / 2 times. //Each loop, ENUM_MACRO is called with the index and a tuple containing the arguments. //A comma is inserted between all expansions of ENUM_MACRO. #define MACRO(...) \ CompileTimeList< \ BOOST_PP_ENUM( \ BOOST_PP_DIV(BOOST_PP_VARIADIC_SIZE(__VA_ARGS__), 2), \ ENUM_MACRO, \ BOOST_PP_VARIADIC_TO_TUPLE(__VA_ARGS__) \ ) \ > //Since it's called #args/2 times, we multiply the index by 2. //All it does is concatenate the first in its pair with the second, and then to Type. #define ENUM_MACRO(z, n, data) \ BOOST_PP_CAT( \ BOOST_PP_CAT( \ BOOST_PP_TUPLE_ELEM(BOOST_PP_MUL(n, 2), data), \ BOOST_PP_TUPLE_ELEM(BOOST_PP_INC(BOOST_PP_MUL(n, 2)), data) \ ), \ Type \ ) MACRO(a, b, c, d) //CompileTimeList< abType , cdType > See it work here. While in its current state, this macro will simply ignore the last argument in an odd-numbered list, it is possible to produce an error with a simple static_assert, or to do something special with the last argument if so desired.
GCC: __attribute__ ((format (printf, x, y)) does not seem to work when function is called using a variadic macro
GCC version ntoarm-gcc (GCC) 4.4.2 I've added 'printf' format attributes to all my functions that wrap printf() and co. They work perfectly fine except when calling the functions using a variadic macro. class Log { [...] void log_fmt(LogLevel level, const std::string& funcName, const char_t * const logFormatStr, ...) __attribute__ ((format (printf, 4, 5))); [...] }; An incorrect direct call like log.log_fmt(Info, "test", "wrong %u", "type"); yields warning: format '%u' expects type 'unsigned int', but argument 5 has type 'const char*' The same incorrect call using a macro yields no warning, however: #define LOGI(MSG, ...) log.log_fmt(Info, __func__, (MSG), __VA_ARGS__) LOGI("wrong %u", "type"); Can I get the warnings to show up in this case too? Have I made a mistake or is this intended behaviour?
This: #include <iostream> #include <cstdio> struct log { static void logf(std::string, std::string, const char*, ...) __attribute__((format (printf, 3, 4))) {} }; #define L(m, ...) log::logf("no", __func__, (m), __VA_ARGS__) int main() { //log::logf("hi", "hi", "test %u", "hi"); L("test %u", "hi"); } works perfectly, in the sense that it gives the correct warning, like below: main.cpp: In function 'int main()': main.cpp:8:61: warning: format '%u' expects argument of type 'unsigned int', but argument 4 has type 'const char*' [-Wformat=] #define L(m, ...) log::logf("no", __func__, (m), __VA_ARGS__) ^ main.cpp:12:5: note: in expansion of macro 'L' L("test %u", "hi"); ^ At global scope: cc1plus: warning: unrecognized command line option "-Wno-undefined-internal" [enabled by default] So, I would guess that the problem is on the position parameters (you have put 4, 5 on your format attribute, when it seems that you should have put 3, 4)....
Forcing preprocessor error with macro
Is there a way that I can force a preprocessor macro in C++ to emit an error? What I would like to do is define a macro UNKNOWN. I'm writing some code for a robot, and I don't yet know where all of the electronics are being plugged in. I'd like to be able to define the ports in some header file, like const int MOTOR_PORT = 1; const int FAN_PORT = 2; //etc. However, when I reach a port that I don't yet know, I want to be able to write something like const int LED_PORT = UNKNOWN; In debug mode, UNKNOWN would just be defined to some arbitrary value, like 0. However, when compiling in release mode, I want it to throw an error when UNKNOWN is used, so that unassigned ports don't end up in the final release. I know I can use the #error directive to force an error, but can I do something similar in a macro? I've seen a solution using static_assert, but I unfortunately can't use C++11 for this platform.
Since #error can't result from a macro expansion, you can ensure that the macro expands to something that must be diagnosed, like a syntax error. For example: #ifdef RELEASE #define UNKNOWN #Invalid_use_of_UNKNOWN #else #define UNKNOWN 0 #endif const int MOTOR_PORT = 1; const int FAN_PORT = 2; const int LED_PORT = UNKNOWN; int main(void) { int x = LED_PORT; } The # character isn't part of C's basic character set, so its appearance outside a comment, character constant, or string literal should always result in an error message. ($ would work, except that accepting $ in identifiers is a common extension. ` would probably also work, but # stands out better.) I've defined the macro so it produces a reasonable error message with gcc: c.c:9:1: error: stray ‘#’ in program c.c:9:22: error: ‘Invalid_use_of_UNKNOWN’ undeclared here (not in a function) and with clang: c.c:9:22: error: expected expression const int LED_PORT = UNKNOWN; ^ c.c:2:17: note: expanded from: #define UNKNOWN #Invalid_use_of_UNKNOWN ^ 1 error generated. (There's a _Pragma operator corresponding to the #pragma directive. It would be nice if there were an _Error operator as well, but there isn't.)
Well, this does not produce a complier error message like #error, but would compile in debug and fail in release: #ifdef _DEBUG # define UNKNOWN 1 #else # define UNKNOWN #endif const int port1 = UNKNOWN; // fail in release
You could make a division by zero which will throw a compiler error: #define UNKNOWN 0/0
The sizeof operator cannot be applied to an incomplete type, so try this: // Declared, but not defined anywhere. struct illegal_use_of_unknown_macro; #define UNKNOWN (sizeof (illegal_use_of_unknown_macro))