1

I am currently adapting a Windows C++ project to make it work on Linux. I defined several macros to print formatted lines to a log file. They are printf-like so I can write this:

WARN("%d::%s<", 42, "baz");

It's pretty easy to print something like:

[thread_id][WARN][/path/to/main.cpp:15][Fri 03/01/2019 10:38:54.408][this_value] 42::baz<

this_value is value of this or NULL if this is not defined (static function, extern "C" function).

My current code is:

#if defined(_WIN32) && !defined(__INTELLISENSE__)
    #define SET_ZIS __if_exists (this) { zis = this; }
#else
    #define SET_ZIS
#endif

#define _LOG(...) \
    do \
    { \
        void *zis = NULL; \
        SET_ZIS \
        GetLoggerInstance()->logMessage(__VA_ARGS__); \
    } while(0)

#define LOG(...) _LOG(level, __FILE__, __LINE__, __func__, zis, __VA_ARGS__)
#define WARN(...) LOG(ILogger_level::LEVEL_WARN, __VA_ARGS__)

Is there a standard way to detect if this exists? Maybe using std::is_* or a SFINAE trick ?

I use extern-ed "C" functions to construct objects ("this" is meaningless) and call members on instanciated objects ("this" is meaningful). "Constructors" are exported in a shared object and dynamically consumed by a C++ project. Doing it that way, I don't have to manage mangled names.

extern "C" int CreateMyClass(std::shared_ptr<MyClass> *newClass);
int CreateMyClass(std::shared_ptr<MyClass> *newClass)
{
  RELAY("(%p)", newClass);
  *newClass = std::make_shared<MyClass>(42, "baz");
  return 0;
}

MyClass::MyClass(int a, char *b)
{
  RELAY("(%d,%s)", a, b);
}

EDIT: Here's a simple test case:

#include <memory> /* For std::shared_ptr */
#define RELAY(...) printf("[%p][%s]\n", this, __func__)

class MyClass
{
public:
  MyClass(int a, const char *b);
  static void test();
};

extern "C" int CreateMyClass(std::shared_ptr<MyClass> *newClass);
int CreateMyClass(std::shared_ptr<MyClass> *newClass)
{
  RELAY("(%p)", newClass);
  *newClass = std::make_shared<MyClass>(42, "baz");
  return 0;
}

MyClass::MyClass(int a, const char *b)
{
  RELAY("(%d,%s)", a, b);
}

void MyClass::test()
{
  RELAY("()");
  printf("some work");
}

int main(int argc, char **argv)
{
  std::shared_ptr<MyClass> newClass;

  int ret = CreateMyClass(&newClass);
  MyClass::test();
  return ret;
}

g++ gives the following errors:

test.c: In function ‘int CreateMyClass(std::shared_ptr<MyClass>*)’:
test.c:2:41: error: invalid use of ‘this’ in non-member function
 #define RELAY(...) printf("[%p][%s]\n", this, __func__)
                                         ^
test.c:14:3: note: in expansion of macro ‘RELAY’
   RELAY("(%p)", newClass);
   ^~~~~
test.c: In static member function ‘static void MyClass::test()’:
test.c:2:41: error: ‘this’ is unavailable for static member functions
 #define RELAY(...) printf("[%p][%s]\n", this, __func__)
                                         ^
test.c:26:3: note: in expansion of macro ‘RELAY’
   RELAY("()");
   ^~~~~

CreateMyClass is not static ("non-member function"), so this is unavailable. Same thing for the static function.

Pika Supports Ukraine
  • 3,612
  • 10
  • 26
  • 42
  • 1
    Are you asking about the C++ keyword `this`, or some non-specific variable? – Tzalumen Mar 07 '19 at 20:08
  • this is a reserved keyword so yes, I'm talking about the C++ this – user7432650 Mar 07 '19 at 21:20
  • I'm blanking on the exact name (and searching for it now) but there is an environment flag that the compiler or preprocessor sets to declare if the compilation unit is being compiled by the C compiler (gcc) or the C++ compiler (g++) – Tzalumen Mar 07 '19 at 21:31
  • https://gcc.gnu.org/onlinedocs/cpp/Standard-Predefined-Macros.html#Standard-Predefined-Macros lists `__cplusplus`, I believe that is the one you want. – Tzalumen Mar 07 '19 at 21:47
  • You don't get it, my "C" function is in a .cpp file. But thanks for the hint. – user7432650 Mar 07 '19 at 21:49
  • `extern "C"` doesn't convert the code to C in a C++ file, `extern "C"` disables C++ name mangling for the function or variable. See https://stackoverflow.com/questions/1041866/what-is-the-effect-of-extern-c-in-c – Tzalumen Mar 07 '19 at 21:56
  • Thanks for the link and sorry for the confusion. We both agree here, I do that only for name mangling as described in my post. – user7432650 Mar 07 '19 at 22:04
  • You could put a line in the class definition of any class supporting this macro usage (e.g. which defines some named function and your macro uses that name, with a global default of the same name) – M.M Mar 07 '19 at 23:52

1 Answers1

-1

The this reference only exists and always exists inside the non-static member functions of a c++ class/struct. It's a pointer to the memory address of the instance of the class a function is operating on. As far as logging is concerned, I'm not sure how you'd use that aside from digging through a memory dump, and I'm not 100% sure that the instance address would even be useful for that.

Tzalumen
  • 652
  • 3
  • 16
  • Okay. So is there a way to detect whether that's the case? – David Schwartz Mar 07 '19 at 22:16
  • The way you are doing things, no, you'll just need two different versions of `LOG` – Tzalumen Mar 07 '19 at 22:24
  • To elaborate: Macros are handled by the preprocessor, and are a blind find & replace operation. The preprocessor doesn't really understand what you want to do, so you will have to tell it, and I'd recommend just making a different macro for use where `this` is present, or reconsidering the benefits of printing the memory address of your class instance to the log. – Tzalumen Mar 07 '19 at 22:35