Let's have
class Item{
public:
Item(int id,const char *name,const char *props=NULL);
};
And I want to write:
ITEM(1,FIRST);
ITEM(2,SECOND, WithSomeProps);
With a macro
#define ITEM(ID,NAME,...) new Item(ID,NAME, #__VA_ARGS__ )
That #__VA_ARGS__ compiles well on gcc but gives an error on VStudio. Is there a solid and portable solution?
I want to have a collection of ITEM() in a .h file that will be included several times with different #definitions of ITEM.
GCC and Visual Studio handle Variadic Macros differently, because Macros are based on the compiler preprocessor (they are expanded at preprocessing time).
One of the difference is how they handle the empty variadic macros.
One of them will allow empty __VA_ARGS__ while the other will cause a compiler error if the __VA_ARGS__ is empty.
On your example the first the line ITEM(1,FIRST) will cause an error at compile time, while working ok on the other .
One workaround for this is to have an empty first argument, so your constructor will be something like :
Item(int id,const char *name,void *allwaysNull, const char *props=NULL);
And then have your Macro initialziations like this
ITEM(1,0,FIRST)
ITEM(2,0,SECOND,WithSomeProps)
What is weird is that from my experience it was GCC that was having problems with empty VA_ARGS for variadic macros...
Related
I am using an API which return int error codes and I decided to build a wrapper error class :
class Error
{
...
public:
Error(int ErrC, const char* UserMessage, const char* FileName, int LineNumber, const char* Function);
char * GetFunction();
...
}
I decided to venture into the world of macros and create a macro to instantiate the class for me :
#define API_ERROR(Code,MSG) API::Error(Code,MSG,__FILE__,__LINE__,__FUNCSIG__)
I then defined a Test function that is called by main
void TestFunc()
{
API::Error Error = API_ERROR(0,"Hello");
std::cout << Error.GetFunction();
}
Using the compiler option to output the post pre-processing results (Properties->C/C++->Preprocessor->Preprocess to a file) yielded
void TestFunc()
{
API::Error Error = API::Error(o,"Hello","...\\main.cpp",30,);
std::cout << Error.GetFunction();
}
I think this does not work because __FUNCSIG__ is only defined inside functions.
I have also tried
#define EMPTY()
#define DEFER(...) __VA_ARGS__ EMPTY()
#define API_ERROR(Code,MSG) API::Error(Code,MSG,__FILE__,__LINE__,DEFER(__FUNCSIG__))
But I think I misunderstood the author of the post.
Is there a way to make this work ?
I am using Visual Studio 2019 Community with the default MSVC++ compiler.
From this VS2019 predefined macro reference
The __FUNCSIG__ macro isn't expanded if you use the /EP or /P compiler option.
[Emphasis mine]
If you preprocess your source then the /EP or /P flags would be set, and the macro won't be expanded. It will only be expanded when actually building your source.
The __FUNCSIG__ macro will probably not be expanded by the preprocessor because it doesn't really know anything about C++ symbols. It might not even be a true preprocessor macro, and could be "expanded" (or replaced) at a later stage in compilation when C++ symbols are known.
I want to test for the use of a constant in a source file and if it is used, stop compilation.
The constant in question is defined in a generic driver file which a number of driver implementations inherit from. However, it's use has been deprecated so subsequent updates to each drivers should switch to using a new method call and not the use of this const value.
This doesn't work obviously
#ifdef CONST_VAR
#error "custom message"
#endif
How can I do this elegantly? As It's an int, I can define CONST_VAR as a string and let it fail, but that might make it difficult for developers to understand what actually went wrong. I was hoping for a nice #error type message.
Any suggestions?
The Poison answer here is excellent. However for older versions of VC++ which don't support [[deprecated]] I found the following works.
Use [[deprecated]] (C++14 compilers) or __declspec(deprecated)
To treat this warning as an error in a compilation unit, put the following pragma near the top of the source file.
#pragma warning(error: 4996)
e.g.
const int __declspec(deprecated) CLEAR_SOURCE = 0;
const int __declspec(deprecated("Use of this constant is deprecated. Use ClearFunc() instead. See: foobar.h"));
AFAIK, there's no standard way to do this, but gcc and clang's preprocessors have #pragma poison which allows you to do just that -- you declare certain preprocessor tokens (identifiers, macros) as poisoned and if they're encountered while preprocessing, compilation aborts.
#define foo
#pragma GCC poison printf sprintf fprintf foo
int main()
{
sprintf(some_string, "hello"); //aborts compilation
foo; //ditto
}
For warnings/errors after preprocessing, you can use C++14's [[deprecated]] attribute, whose warnings you can turn into errors with clang/gcc's -Werror=deprecated-declarations .
int foo [[deprecated]];
[[deprecated]] int bar ();
int main()
{
return bar()+foo;
}
This second approach obviously won't work for on preprocessor macros.
For a parameter pack I need a macro which can take any number of parameters (types actually), which works cross platform. This code works nicely with GCC, LLVM and MSVC (after the preprocessor had been reworked to support the ## sequence (see Behavior 4 [comma elision in variadic macros]
):
class A {};
class B: A {};
class C: A {};
class D: A {};
template<typename... Interfaces>
class Aggregator: public Interfaces... {
};
#define INNER(...) typedef Aggregator<__VA_ARGS__> AGG;
#define ENVIRONMENT(...) INNER(B, C, ## __VA_ARGS__)
ENVIRONMENT(D)
The problem here is the empty-parameters case (ENVIRONMENT()). As I cannot use C++20 yet (which comes with the __VA_OPT__() token sequence, I have to find a solution that requires at most C++17. GCC + LLVM have no problem with an empty parameter list, however MSVC insists on at least one parameter for the comma elision to work.
What is required to make this construct also work fully with MSVC?
Update: It turns out the empty-parameter case doesn't work for GCC either: https://godbolt.org/z/1zKZO- .
Here's an approach that actually does what you asked for... I've predefined this to work with MSVC, gcc, and clang (to work with just gcc and clang, or just MSVC, would be simpler).
This implements OPTIONAL, which expects a tuple (parenthesized tokens) as the first argument. When OPTIONAL is called with just an empty second argument, it expands to nothing; otherwise, it will expand to the unwrapped version of the first argument. The end result is a kind of analog to (but certainly not equivalent to) C++20's __VA_OPT__.
The following is the OPTIONAL implementation, and support macros:
#define GLUE(A,B) GLUE_C(GLUE_I,(A,B))
#define GLUE_C(A,B) A B
#define GLUE_I(A,B) A##B
#define FIRST(...) FIRST_C(FIRST_I,(__VA_ARGS__,))
#define FIRST_C(A,B) A B
#define FIRST_I(X,...) X
#define THIRD(...) THIRD_C(THIRD_CC,(THIRD_I,(__VA_ARGS__,,,)))
#define THIRD_C(A,B) A B
#define THIRD_CC(A,B) A B
#define THIRD_I(A,B,C,...) C
#define COUNT(...) COUNT_C(COUNT_I,(__VA_ARGS__,9,8,7,6,5,4,3,2,1,))
#define COUNT_C(A,B) A B
#define COUNT_I(_,_9,_8,_7,_6,_5,_4,_3,_2,X,...) X
#define DISCARD_ARGUMENTS(...)
#define OPTIONAL(APPLY_,...) \
THIRD(GLUE(OPTIONAL_SHIFT_IF_1_IS_,COUNT(__VA_ARGS__)),\
OPTIONAL_SINGLE_CASE,\
APPLY_OPTION) \
(APPLY_,__VA_ARGS__)
#define OPTIONAL_SHIFT_IF_1_IS_1 ,
#define OPTIONAL_SINGLE_CASE(APPLY_,...) \
THIRD(OPTIONAL_SHIFT_TEST __VA_ARGS__ (0_UNLOCK), \
DISCARD_ARGUMENTS, \
APPLY_OPTION)(APPLY_,)
#define OPTIONAL_SHIFT_TEST(...) GLUE(OPTIONAL_APPLY_SHIFT_TEST_,FIRST(__VA_ARGS__))
#define OPTIONAL_APPLY_SHIFT_TEST_0_UNLOCK ,
#define APPLY_OPTION(A,...) APPLY_OPTION_C(APPLY_OPTION_I,A)
#define APPLY_OPTION_C(A,B) A B
#define APPLY_OPTION_I(...) __VA_ARGS__
The core mechanism is an "indirect third macro"; the idea here is to generate a first argument that applies some "test" which, if something of interest shows up, generates a comma, which shifts the second argument to the third position just prior to selection.
This is used twice by OPTIONAL; if there's one argument, there's a next stage test to see if that argument has no tokens. This test injects the argument's tokens between OPTIONAL_SHIFT_TEST and (0_UNLOCK); if there are no tokens, that makes a call, and this macro will generate an object macro that creates the shifting comma. This indirection is intentional, allowing parentheses to be in the first argument without a false detection (see demo).
What is required to make this construct also work fully with MSVC?
...built into the indirection layers of all macros are "caller macros"; here, they all have _C in the name, take two parameters A and B, and simply expand to A B; their use is always to separate a macro name from a macro argument set. Those address MSVC. Were I actually trying to target MSVC (for whatever reason), only one such caller would be necessary; by making a caller for each macro set, however, we get to make this work for MSVC and gcc/clang as well. (ETA: THIRD requires two caller indirections; once for the varying arguments in third itself, and the other to properly interpret the expanded first argument's commas, since that's the whole point of the THIRD macro).
Note that this doesn't rely on any of the compiler specific comma elision tricks.
Finally... with OPTIONAL in place, all you need to do is this:
#define INNER(...) typedef Aggregator<__VA_ARGS__> AGG;
#define ENVIRONMENT(...) INNER(B, C OPTIONAL((,),__VA_ARGS__) __VA_ARGS__)
Godbolt demos
gcc/clang: https://godbolt.org/z/GK3Huh
MSVC: https://godbolt.org/z/kvRPau
what about something like this?
template <class... X>
using INNER = Aggregator<B, C, X...>;
#define ENVIRONMENT(...) typedef INNER<__VA_ARGS__> AGG
ENVIRONMENT(); // Or ENVIRONMENT(D)
Hope this helps...
I am using Plog in my project for logging. I also use resharper c++.
There are several macros used for logging that make it less verbose. One of the macros always gets marked as an issue with Resharper but I don't know why.
The macro is LOGD which expands to
if (!plog::get<0>() || !plog::get<0>()-
>checkSeverity(plog::debug)) {;} else (*plog::get<0>()) +=
plog::Record(plog::debug, __FUNCTION__, 151, "",
__if_exists(this) { this } __if_not_exists(this) { 0 }) <<
"Message";
The warning points to the usage of "this" as it expects an identifier.
The method it is calling has a signature of:
Record(Severity severity, const char* func, size_t line, const char* file, const void* object)
: m_severity(severity), m_tid(util::gettid()), m_object(object), m_line(line), m_func(func), m_file(file)
Does anyone have experience with this an know how to modify the macro so it does not complain?
I've filed RSCPP-22423, we'll try to fix this in one of the 2018.2 EAP builds. Until then, you can redefine the PLOG_GET_THIS macro for ReSharper C++ like this:
#include <plog/Log.h>
#if defined(__RESHARPER__)
#define PLOG_GET_THIS() reinterpret_cast<void*>(0)
#endif
Or similarly update the original definition of PLOG_GET_THIS (it already checks for the similar __INTELLISENSE__ macro to determine if __if_exists can be used).
Given this test program:
#include <cassert>
#include <string>
#include <type_traits>
const std::string& const_string = "bla";
std::string const & string_const = "blabla";
static_assert(std::is_same<decltype(const_string), decltype(string_const)>::value, "Uhoh");
int main()
{
assert(std::is_same<decltype(const_string), decltype(string_const)>::value);
}
Which asserts that two types are the same at compile-time and at runtime using C's assert. All of Clang, MSVC2015, and GCC report the same error, so I'm quite sure it's me:
main.cpp:13:49: error: too many arguments provided to function-like macro invocation
assert(std::is_same<decltype(const_string), decltype(string_const)>::value);
^
/usr/include/assert.h:91:10: note: macro 'assert' defined here
# define assert(expr) \
^
I'm just not seeing two arguments in the assert. What's more, the static_assert works just fine... So what is going on here?
The C preprocessor does not recognise C++ template syntax, therefore template brackets < and > are not viewed as grouping tokens by the preprocessor, they're seen as simple characters.
This means that the preprocessor will view the comma between the template parameters as a macro parameter separator, like this:
assert(
std::is_same<decltype(const_string),
decltype(string_const)>::value);
To force the preprocessor to see your expression as a single statement, simply wrap your assert parameter in an additional set of brackets:
assert((std::is_same<decltype(const_string), decltype(string_const)>::value));
static_assert doesn't have this limitation because it is a C++ keyword, rather than a preprocessor macro like assert(). This means it fully supports C++ syntax and sees the template parameters correctly.
It's because of the < and > tokens. They mess up the preprocessor. Remember that assert is a macro, not a function.
Do this (add an extra set of parentheses):
assert((std::is_same<decltype(const_string), decltype(string_const)>::value));
^ ^