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.
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.
Related
I have some well-formed code looks like this:
NAMESPACE_BEGIN(Foo)
inline void test() {
string s = xxx;
}
NAMESPACE_END(Foo)
So, is there any portable tricks by using the NAMESPACE_BEGIN() macro to obtain namespace name "Foo" in test()?
I'm thinking of something like this, but it would surely cause symbol redefinition:
#define NAMESPACE_BEGIN(x) \
namespace x { \
inline const char *_curNamespace() { \
return #x; \
}
#define NAMESPACE_END(x) \
}
There's also a workaround looks like this, but that's not very convenient
#define NAMESPACE_NAME Foo
// using a header file so that we can use #ifdef guard
#include "MyNamespaceBegin.h"
...
#include "MyNamespaceEnd.h"
EDIT:
Why I need this:
I'm using much of macro to generate codes to achieve some
dynamic reflection logic (yes, not static template reflection),
it's all right within class scope by using static member function,
but does not work for namespaces
Why not to manually declare the name getter once:
What I want is something like this:
// the global default version
const char *_curNamespace() {return "";}
namespace X {
// the local namespace version
const char *_curNamespace() {return "X";}
// some verbose reflection register code
...
registerSomething(_curNamespace());
...
}
Of course, all of the verbose register code should be generated by macro
And, app level user should not care about the _curNamespace(),
so, I want to simplify the user's usage,
by using a custom NAMESPACE_BEGIN(xxx) macro at any case
If you are still curious about what I'm doing,
check this: https://github.com/ZFFramework/ZFFramework
I'm using lots of tricks to achieve fully dynamic reflection in pure C++,
to achieve some of my fancy thoughts,
for now, this project is just for fun,
I have no idea whether it has practicability
EDIT2:
For now, I think the best workaround should be like this:
#define NAMESPACE_BEGIN(ns) \
namespace ns { \
extern const char *__curNS();
#define NAMESPACE_END(ns) \
}
#define NAMESPACE_REG(ns) \
const char *__curNS() {return #ns;}
app level users still only need to care about NAMESPACE_BEGIN
NAMESPACE_REG must be declared exactly once, in source file
if not, undefined symbol would happen
if more than once, duplicated symbol would happen
although it's annoying and sometimes you need additional source file
to hold the NAMESPACE_REG,
the strict rule should prevent user from forgetting the ugly workaround
You are making much fuss over something that is trivial to implement.
First of all, use of NAMESPACE_BEGIN and NAMESPACE_END seems unnecessary to me. I don't see how that is more readable or useful than
namespace Foo
{
}
If getting the name of the namespace is important/useful, add a trivial function.
namespace Foo
{
inline std::string get_name() { return "Foo"; }
}
Small sized real world applications need thousands of lines of code. Large sized real world applications need millions of lines of code. From that perspective, implementing a one line inline function is a very minor task.
This solution employs a bit of preprocessor magic and has these features:
Namespace is mentioned only once
Access to a macro containing the unquoted name
Access to a macro containing the quoted name
Support for repeating the same namespace
Support for different namespaces
Misuse of the BEGIN/END macros is detected
Cleanup, i.e. no extra macros defined outside the BEGIN/END block
It does not support nested namespaces.
Example of usage:
#include "framework.hpp"
#define NAMESPACE_NAME Foo
#include NAMESPACE_BEGIN
// Here you have access to NAMESPACE_NAME (unquoted, i.e. Foo)
// and also to NAMESPACE_NAME_STRING (quoted, i.e. "Foo")
#include NAMESPACE_END
// NAMESPACE_NAME and NAMESPACE_NAME_STRING do not exist
// outside the block, so they cannot be misused
// Different namespaces in the same TU are supported
#define NAMESPACE_NAME Bar
#include NAMESPACE_BEGIN
inline std::string f()
{
return NAMESPACE_NAME_STRING;
}
#include NAMESPACE_END
// Repeating the same namespace is also supported
#define NAMESPACE_NAME Foo
#include NAMESPACE_BEGIN
inline std::string f()
{
return NAMESPACE_NAME_STRING;
}
#include NAMESPACE_END
The implementation follows:
framework.hpp
#pragma once
#define NAMESPACE_BEGIN "framework_namespace_begin.hpp"
#define NAMESPACE_END "framework_namespace_end.hpp"
framework_namespace_begin.hpp
#ifndef NAMESPACE_NAME
#error "NAMESPACE_NAME not defined"
#endif
#define NAMESPACE_IN_NAMESPACE 1
#define NAMESPACE_NAME_DO_STR(X) #X
#define NAMESPACE_NAME_STR(X) NAMESPACE_NAME_DO_STR(X)
#define NAMESPACE_NAME_STRING NAMESPACE_NAME_STR(NAMESPACE_NAME)
namespace NAMESPACE_NAME {
framework_namespace_end.hpp
#ifndef NAMESPACE_IN_NAMESPACE
#error "NAMESPACE_IN_NAMESPACE not defined"
#endif
}
#undef NAMESPACE_NAME
#undef NAMESPACE_NAME_STRING
#undef NAMESPACE_IN_NAMESPACE
You know what? I think I might just have a viable solution for this. It's actually very simple, and it's very close to the OP's original suggestion (which really only had the problem of a potential duplicate definition if you wanted to open the namespace twice in the same translation unit). You just have to think a bit laterally and not be too precious about seeing your namespaces being bracketed by macros instead of curly braces.
So let me just lay it out here, because there's really nothing to it, and then I'll explain why I personally happen to like it.
Code:
Macros:
#define DECLARE_NAMESPACE(ns) \
namespace ns {\
static constexpr const char *_curNamespace = #ns; \
}
#define BEGIN_NAMESPACE(ns) \
namespace ns { \
static_assert (ns::_curNamespace, "BEGIN_NAMESPACE: namespace has not been declared");
#define END_NAMESPACE }
Sample code:
#include <iostream>
DECLARE_NAMESPACE (Foo)
BEGIN_NAMESPACE (Foo)
void print_namespace_name () { std::cout << _curNamespace << "\n"; }
END_NAMESPACE
BEGIN_NAMESPACE (Foo)
void another_print_namespace_name () { std::cout << _curNamespace << "\n"; }
END_NAMESPACE
DECLARE_NAMESPACE (Bar)
BEGIN_NAMESPACE (Bar)
void print_namespace_name () { std::cout << _curNamespace << "\n"; }
DECLARE_NAMESPACE (BarBar)
BEGIN_NAMESPACE (BarBar)
void print_namespace_name () { std::cout << _curNamespace << "\n"; }
END_NAMESPACE
END_NAMESPACE
int main ()
{
Foo::print_namespace_name ();
Foo::another_print_namespace_name ();
Bar::print_namespace_name ();
Bar::BarBar::print_namespace_name ();
}
Output:
Foo
Foo
Bar
BarBar
Now this is obviously very straightforward to implement and also easy to use and has no obvious limitations. In particular, it can handle nested namespaces (as shown in the code above) and opening a namespace twice in the same compilation unit also works (again, this is shown in the code snippet).
But, but, but, don't we still have to type in the name of the namespace twice, and wasn't that the very thing we were trying to avoid to eliminate typos?
Well, sure, we have to type the name in twice, but so what, live with it. Point is, with this particular set of macros, the compiler will now catch any typos for us. Let's prove that by deliberately putting one in. So this:
DECLARE_NAMESPACE Whoops
BEGIN_NAMESPACE whoops
END_NAMESPACE
Generates this (I couldn't find a better way of formulating the static_assert, sorry):
prog.cc:12:24: error: '_curNamespace' is not a member of 'whoops'
static_assert (ns::_curNamespace, "BEGIN_NAMESPACE: namespace has not been declared");
^~~~~~~~~~~~~
prog.cc:27:5: note: in expansion of macro 'BEGIN_NAMESPACE'
BEGIN_NAMESPACE (whoops)
^~~~~~~~~~~~~~~
And more importantly this (and this is why we need the BEGIN_NAMESPACE macro):
DECLARE_NAMESPACE (Bar)
BEGIN_NAMESPACE (Bar)
DECLARE_NAMESPACE (BarWhoops)
BEGIN_NAMESPACE (Barwhoops)
END_NAMESPACE
END_NAMESPACE
Generates this:
prog.cc:12:24: error: '_curNamespace' is not a member of 'Bar::Barwhoops'
static_assert (ns::_curNamespace, "BEGIN_NAMESPACE: namespace has not been declared");
^~~~~~~~~~~~~
prog.cc:42:5: note: in expansion of macro 'BEGIN_NAMESPACE'
BEGIN_NAMESPACE (Barwhoops)
^~~~~~~~~~~~~~~
Which is just dandy.
So, you know, what's not to like?
Live demo - uncomment line 3 to see those compiler errors.
you can use a variable and change its value with 'NAMESPACE_BEGIN' and 'NAMESPACE_END'
the variable __name represent the current full namespace position
like "abc::def::detail"
std::string __name = "";
std::string & __append(std::string & str, const char * ptr) {
if (!str.empty()) {
str.append("::");
}
str.append(ptr);
return str;
}
std::string & __substr(std::string & str, const char * ptr) {
if (str.length() == strlen(ptr)) {
str.clear();
} else {
str = str.substr(0, str.length() - strlen(ptr) - 2);
}
return str;
}
#define NAMESPACE_NAME __name
#define CONCATENATE_DIRECT(s1, s2) s1##s2
#define CONCATENATE(s1, s2) CONCATENATE_DIRECT(s1, s2)
#ifdef _MSC_VER
# define ANONYMOUS_VARIABLE(str) CONCATENATE(str, __COUNTER__)
#else
# define ANONYMOUS_VARIABLE(str) CONCATENATE(str, __LINE__)
#endif
#define APPEND_NAME(x) std::string ANONYMOUS_VARIABLE(__start_name) = __append(__name, #x)
#define SUBSTR_NAME(x) std::string ANONYMOUS_VARIABLE(__end_name) = __substr(__name, #x)
#define NAMESPACE_BEGIN(x) \
namespace x { \
APPEND_NAME(x);
#define NAMESPACE_END(x) \
SUBSTR_NAME(x); \
}
then you can use the NAMESPACE_NAME macro for the full name or you can extract the last name from it
Here's a way. The core idea came from the line of thought:
Q: How can I define multiple things with the same name accessible from the same scope?
A: Make them all functions with different parameter types. And if all of them have identical bodies, it doesn't matter which one gets called.
Q: How can I generate an unlimited set of different parameter types?
A: A class template.
Q: How can I make sure a call to that set of overloaded functions will never be ambiguous?
A: Make sure the binary relation "is implicitly convertible from" is a complete ordering on the parameter types, and use a unique minimal element for the argument type.
#include <type_traits>
#include <functional>
struct NamespaceHandleObj {
template <const NamespaceHandleObj* Handle1, const NamespaceHandleObj* Handle2>
struct less : public std::bool_constant<std::less<>{}(Handle1, Handle2)> {};
};
template <>
struct NamespaceHandleObj::less<nullptr, nullptr> : public std::false_type {};
template <const NamespaceHandleObj* Handle1>
struct NamespaceHandleObj::less<Handle1, nullptr> : public std::false_type {};
template <const NamespaceHandleObj* Handle2>
struct NamespaceHandleObj::less<nullptr, Handle2> : public std::true_type {};
template <const NamespaceHandleObj* Handle>
struct NamespaceParamType
{
constexpr NamespaceParamType() noexcept = default;
template <const NamespaceHandleObj* Other,
typename = std::enable_if_t<NamespaceHandleObj::less<Other, Handle>::value>>
constexpr NamespaceParamType(NamespaceParamType<Other>) noexcept {}
};
#define NAMESPACE_UTILS_TOSTR1(x) #x
#define NAMESPACE_UTILS_TOSTR(x) NAMESPACE_UTILS_TOSTR1(x)
#define BEGIN_NAMESPACE(ns) \
namespace ns { \
namespace { \
constexpr NamespaceHandleObj namespace_handle_{}; \
constexpr const char* current_ns_(
NamespaceParamType<&namespace_handle_>) noexcept \
{ return NAMESPACE_UTILS_TOSTR(ns); } \
}
#define END_NAMESPACE }
#define CURRENT_NAMESPACE (current_ns_(NamespaceParamType<nullptr>{}))
The code above is C++17, but it wouldn't be hard to port it to previous versions, even all the way to C++03.
I have a class in my C++ code similar to the following:
class myClass
{
public:
void method1(int a, int b, std::string str);
};
Other classes can instantiate an object from myClass and call method1.
void caller()
{
obj1->method1(12, 4, "sample");
}
I want to log all the callers of myClass (function name, file name, line number). One possible solution is this:
class myClass
{
public:
method1(int a, int b, std::string str, const char *_function = __builtin_FUNCTION(), const char *_file = __builtin_FILE(), int _line = __builtin_LINE());
};
which is using __builtin_xxx as default arguments. This solution has multiple shortcomings:
It is an ugly solution
__builtin_xxx is only available in gcc version > 4.8
We have to add three default parameters to method1
IDE shows the default parameters on auto-completion that are not meant to be provided by the user!
Another solution is using __LINE__, __FILE__ and __func__ that is basically very similar to the previous solution. They are not defined outside of function scope, and they should be used like this:
void caller()
{
obj1->method1(12, 4, "sample", __func__, __FILE__, __LINE__);
}
Here is a working example for both solutions.
Is there any better solution to log the caller when the user calls method1 on myClass object. By a better solution I specifically mean not to change the method1's declaration by adding three more parameters!
Another ugly solution, but I'm using...
Use macros to automatically add __LINE__ __FILE__ ...etc. things into parameters.
For example
#define Method(param0,param1) Method(param0,param1,__LINE__)
It has a lot of problem, if you want macros work as normal function, you has to do a lot of things, and it still may not works.
I use it to help me log errors.
Looks like a duplicate of Print the file name, line number and function name of a calling function - C Prog
I'd pass the data to the function through parameters (maybe get the help of a macro)
int info(const char *fname, int lineno, const char *fxname, ...) { /* ... */ }
int debug(const char *fname, int lineno, const char *fxname, ...) { /* ... */ }
int error(const char *fname, int lineno, const char *fxname, ...) { /* ... */ }
And to call them
info(__FILE__, __LINE__, __func__, ...);
debug(__FILE__, __LINE__, __func__, ...);
error(__FILE__, __LINE__, __func__, ...);
Note: __func__ is C99; gcc, in mode C89 has __FUNCTION__
I have this pieces of code:
class DLL_API MyClassWrapper
{
private:
MyClass * m_myClass;
public:
MyClassWrapper(SIZE inputSize);
~MyClassWrapper();
inline int OutputSize();
}
typedef std::shared_ptr<MyClassWrapper> MyClassWrapperPtr;
extern "C"
{
DLL_API MyClassWrapperPtr CreatreMyClassWrapper(SIZE inputSize)
{
return std::make_shared<MyClassWrapper>(inputSize);
}
}
But it doesn't work, with error:
Error 1 error C2526: CreatreMyClassWrapper: C linkage function cannot return C++ class 'std::shared_ptr<_Ty>'
I understand the problem, but how can I fix it?
The options that I can see are:
1- Don't pass a shared pointer. which means that DLL user should delete the pointer after they used it.
2- Don't use extern "C" : which means that I must use mangled names.
Is there any other solution?
Straight to the point, to return C++ object from C function - just returns it via output arguments:
extern "C"
{
DLL_API void CreatreMyClassWrapper(SIZE inputSize, SomeClass* outputPtr)
{
*outputPtr = SomeClass(....);
}
}
In your example SomeClass == MyClassWrapperPtr, so:
extern "C"
{
DLL_API void CreatreMyClassWrapper(SIZE inputSize, MyClassWrapperPtr* outputPtr)
{
*outputPtr = make_shared<MyClassWrapper>(inputSize);
}
}
Consider however to change your interface a little, because in current shape you need to be sure that your applications and DLLs shall use the same compiler, linker, settings, libraries...*
You might want to export Create and Delete from your DLL to be sure memory management will occur in your DLL (this is based on this answer:
DLL
extern "C"
{
DLL_API MyClassWrapper* CreateMyClassWrapper(SIZE inputSize)
{
return new MyClassWrapper(inputSize);
}
DLL_API void DeleteMyClassWrapper(MyClassWrapper* wrapper)
{
delete wrapper;
}
}
Application
shared_ptr<MyClassWrapper> myClassWrapper(CreateMyClassWrapper(inputSize),
DeleteMyClassWrapper);
The following code explains the situation I encountered:
#ifdef __cplusplus
namespace ns
{
class pod
{
const short foo;
const char bar;
public:
pod(short f,char b):foo(f),bar(b){}
char foobar();
};
}
#else
typedef struct pod pod;
#endif
#ifdef __cplusplus
extern "C"{
#endif
extern pod* pod_new(short f, char b);//BANG!!!
extern char pod_foobar(pod* p); //BANG!!!
#ifdef __cplusplus
}
#endif
I can't put the C linkage functions inside the namespace ns, or the C client won't find their definitions. When I pull them out of the namespace, the C++ definition won't compile either,all because of the pod solution, which I learned from C++ FAQ Lite,it's only a preprocessor trick. And sadly, this trick couldn't deal with namespaces.
What am I supposed to do? Should I throw out all type-safety, and replace pod* with void*, or is there a nicer solution for this kinda situation? Any advices? Please!
I'd just give two different declarations for the functions
for C++:
extern "C" ns::pod* pod_new(short f, char b);
extern "C" char pod_foobar(ns::pod* p);
and for C:
typedef struct pod pod;
extern pod* pod_new(short f, char b);
extern char pod_foobar(pod* p);
But if this doesn't satisfy you, for C++ you could also have a typedef
typedef ns::pod ns_pod;
for C
typedef struct ns_pod ns_pod;
and then have the same common function prototype.
extern ns_pod* pod_new(short f, char b);
extern char pod_foobar(ns_pod* p);
Edit: In C the struct pod or struct ns_pod is an incomplete type, so in C directly you could never do anything that uses the fields or asks for its size. The difference between a pointer to an incomplete type and a void* is that you can only assign such a struct pointer to another struct pointer of the same incomplete type.
typedef struct ns_pod2 pod2;
ns_pod* q = pod_new(...); // valid
ns_pod2* r = pod_new(...); // a constraint violation! (= compiler error)
The second one would need an explicit cast, if you want to insist. This is one of the reasons why casts are frowned upon by many C programmers.
I figured it out myself :) by checking the symbols of the obj files with nm.
It turns out that the C++ namespaces have no effects on functions with C linkage, therefore I can rewrite the code above like this:
#ifdef __cplusplus
namespace ns
{
class pod
{
const short foo;
const char bar;
public:
pod(short f,char b):foo(f),bar(b){}
char foobar();
};
}
#else
typedef struct pod pod;
#endif
#ifdef __cplusplus
namespace ns{
extern "C"{
#endif
pod* pod_new(short f, char b);
char pod_foobar(pod* p);
void pod_free(pod* p);
#ifdef __cplusplus
}
}
#endif
This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
Name mangling of c++ classes and its member functions?
I created a Visual C++ dll. It is working and i can call my Thrust methods from cuda through this dll in c#.
The only problem is, that i am not able to unmangle the function names. I would like to have the normal names so i would not need to use an Entrypoint with the convention.
Here is my code.
This is my header
//ThrustCH.h
#pragma once
enter code here__declspec(dllexport) class ThrustFuncs
{
__declspec(dllexport) static int maxValueThrust(int *data, int N);
__declspec(dllexport) static double maxValueThrust(double *data, int N);
__declspec(dllexport) static int* sort(int* data, int N);
__declspec(dllexport) static double* sort(double* data, int N);
__declspec(dllexport) static int simple(int N);
};
This is my cpp
// thrustDLL.cpp : Defines the exported functions for the DLL application.
//
#include "stdafx.h"
#include "thrustH.h"
#include "thrustCH.h"
extern "C" {
__declspec(dllexport) int ThrustFuncs::maxValueThrust(int *data, int N){
return thrustH::maxValue(data,N);
}
__declspec(dllexport) double ThrustFuncs::maxValueThrust(double *data, int N){
return thrustH::maxValue(data,N);
}
__declspec(dllexport) int* ThrustFuncs::sort(int* data, int N){
return thrustH::sort(data,N);
}
__declspec(dllexport) double* ThrustFuncs::sort(double* data, int N){
return thrustH::sort(data,N);
}
__declspec(dllexport) int ThrustFuncs::simple(int N){
return N;
}
}
I tried to use extern "C" and __declspec(dllexport) almost everywhere put i guess I'm doing something wrong. Could you help me please?
It seems you are trying to export C++ functions but want them to have a C name.
There is no direct way to do that, mainly because it doesn't make sense.
C doesn't have classes (or namespaces for that matter) and those usually are involved in the C++ name mangling. That is, don't write the functions you intend to export with a C name decoration in a class declaration.
You can, however, still write C functions (in an extern "C" block) in which you call your C++ functions, methods, or classes.
Something like:
class foo
{
static int bar(const std::string& str) { return static_cast<int>(str.size()); }
}
extern "C"
{
int bar(const char* str)
{
// Call C++ version of the function.
try
{
return foo::bar(str);
}
catch (std::exception&)
{
// Handle it somehow
}
}
}
You may want to use __cdecl for the functions that you want to export which will not mangle the names.
Refer: /Gd, /Gr, /Gz (Calling Convention)