Is There a _Class_ Macro in C++

Is there a __CLASS__ macro in C++?

The closest thing there's is to call typeid(your_class).name() - but this produces compiler specific mangled name.

To use it inside class just typeid(*this).name()

Is there C macro for member methods?

Yes, it is possible if you're able to use the variadic macros feature:

// ...

#define CALL_METHOD(x, y, ...) x->y(x, ##__VA_ARGS__)

struct string
{
char *value;
size_t (*size)(struct string *);
int (*compare)(struct string *, struct string *);
int (*set_value)(struct string *, const char *);
};

// ...

int main()
{
// ...

CALL_METHOD(s1, set_value, "foo");
CALL_METHOD(s2, set_value, "bar");

printf("s1->size(s1) = %zu;\n", s1->size(s1));
printf("CALL_METHOD(s1, size) = %zu;\n", CALL_METHOD(s1, size));
printf("s1->compare(s1, s2) = %d;\n", s1->compare(s1, s2));
printf("CALL_METHOD(s1, compare, s2) = %d;\n", CALL_METHOD(s1, compare, s2));

// ...
}

What is the meaning of macro in front of class definition in c++

It's not standard C++ (unless the macro evaluates to whitespace, or you're using attributes) but it's required by some compilers when targeting certain platforms.

A common occurrence is when building a dynamic linked library (dll) for Windows. OCTINTERP_API is likely set to __declspec(dllexport) when building the dll, and __declspec(dllimport) when using the dll.

(See https://en.cppreference.com/w/cpp/language/classes)

Declare a class and member variables using macros in C++

#define NEW_CLASS(name_, seq_) \
class name_ : public ClassBase \
{ \
public: \
IMPL_NEW_CLASS_end(IMPL_NEW_CLASS_decl_loop_a seq_)\
\
std::vector<void *> fun() \
{ \
return { IMPL_NEW_CLASS_end(IMPL_NEW_CLASS_list_loop_a seq_) }; \
} \
};

#define IMPL_NEW_CLASS_end(...) IMPL_NEW_CLASS_end_(__VA_ARGS__)
#define IMPL_NEW_CLASS_end_(...) __VA_ARGS__##_end

#define IMPL_NEW_CLASS_decl_loop_a(...) ::std::type_identity_t<__VA_ARGS__> IMPL_NEW_CLASS_decl_loop_b
#define IMPL_NEW_CLASS_decl_loop_b(name_) name_; IMPL_NEW_CLASS_decl_loop_a
#define IMPL_NEW_CLASS_decl_loop_a_end

#define IMPL_NEW_CLASS_list_loop_a(...) IMPL_NEW_CLASS_list_loop_b
#define IMPL_NEW_CLASS_list_loop_b(name_) &name_, IMPL_NEW_CLASS_list_loop_a
#define IMPL_NEW_CLASS_list_loop_a_end


NEW_CLASS(ClassA, (int)(a) (float)(b) (char)(c))

I've used the (a)(b)(c) syntax for lists, because AFAIK only those lists can be traversed without generating a bunch of repetitive boilerplate macros. (can't do that with a, b, c)

I've wrapped the type in std::type_identity_t<...> to allow arrays, function pointers, etc (int[4] x; is invalid, but std::type_identity_t<int[4]> x; is ok).

I chose this specific syntax because the types can contain commas, so e.g. (type,name)(type,name) wouldn't be viable, because it's tricky to extract the last element from a comma-separated list (consider (std::map<int,float>,x), which counts as a comma-separated list with 3 elements).

(name,type)(name,type), on the other hand, would be viable (extracting the first element of a list is simple), but it doesn't look as good. If you go for this syntax, note that the loops still have to use at least two macros each (_a and _b in my example), even if the two would be the same (a single-macro loop doesn't work because of the ban on recursive macros). The loops would also need two _end macros each, not one.

With C++17 only, Is it possible to create a macro which accepts class and method name and returns true if such a method exists in the class?

Yes, this is possible, though it does require a macro AFAIK, but you seem to not mind that.

We of course need some SFINAE context because that is the only place where we are allowed to write syntax errors. But creating in-place templates is tricky because local classes cannot contain them, so even if(class { /* template magic */}x; <something with x>) C++17 feature won't help much.

Since C++14 there is one feature that I am aware of which does allow defining types with templated methods inside an expression - templated lambdas.

I took inspiration from std::visit with overloaded set and SFINAE test using overloads:

template<class... Ts> struct overloaded : Ts... { using Ts::operator()...; };
// explicit deduction guide (not needed as of C++20)
template<class... Ts> overloaded(Ts...) -> overloaded<Ts...>;

#define HAS_METHOD(class_type,method) \
overloaded { \
[](auto* arg, decltype(&std::decay_t<decltype(*arg)>::method) ptr) constexpr \
{ return true;}, \
[](auto* arg, ...) constexpr \
{ return false;} \
}((class_type*)nullptr,nullptr)

#include <iostream>

int main()
{
struct S{void call();};
if constexpr (HAS_METHOD(S,call))
std::cout << "S has a call\n";
else
std::cout << "S does not have a call\n";

if constexpr (HAS_METHOD(S,call2))
std::cout << "S has a call2\n";
else
std::cout << "S does not have a call2\n";
}

Output(Godbolt):

S has a call
S does not have a call2

Explanation

Overload-based SFINAE

Based on this answer, one can base SFINAE on overloading resolution between templated functions. Nice property of this is there is no need for specialization.

Here I used this approach for lambda's templated operator(). The code boils down to something like this:

struct overload{
template<class T>
bool operator()(T * arg, decltype(&std::decay_t<decltype(*arg)>::call) ptr)
{
return true;
}
template<class T>
bool operator()(T * arg, ...)
{
return false;
}
};

When we call overload{...}((S*)nullptr,nullptr), T is deduced to be S from the first parameter. This effectivelly gets rids of the templated code while still being in SFINAE context. The first (auxillary) parameter is necessary because lambdas do not have template <typename S> prior to C++20, also to obtain the type, one has to use decltype(arg). std::decay_t is required because dereferencing a pointer returns a reference and T&::call is never valid syntax.

Note that one cannot use std::declval here because the context is evaluated. Pointer it is then, we won't actually dereference it anywhere. Now

  1. If S::call is valid, the second parameter is of type "pointer to a member function with call's signature". Of course nullptr is a valid value for any pointer. Because this overload is more specific than ...(anything valid is), it is chosen and true is returned in constexpr manner.
  2. If S::call constitues a syntax error, the first overload is discarded by SFINAE, the second still matches because ... will match nullptr and the first argument could still be deduced. In that case we return false.

Overloading

To build the required set of overloads from lambdas in one expression, one can use parameter pack expansion and inheritance of methods which is exactly what this line from std::visit helper does:

template<class... Ts> struct overloaded : Ts... { using Ts::operator()...; };

Then the macro itself just construct a temporary instance of this class, ctor is used to initialize the base classes = pass the lambdas. After that, the temporary is immedietely called with ((S*)nullptr,nullptr) arguments.

Refer to class in C++ macro

This is closely related this question. The answer is that you can not (yet) write something that uses the type of the class definition you are in. You will have to write a macro that includes the start of the class definition (i.e the struct Foo) and some mechanism to facilitate the typedef.

Class name macro

Use a macro to define the class:

#define CLASS_WITH_NAME(name)  name { const char * __NAME = #name;

class CLASS_WITH_NAME(class_name) // No "{" here!

Ugly hack but the best I can think of.

macro in front of class definition in c++

Checking OpenRTI's source code, it looks like it is simply indicating that the simply is meant to be hidden when compiled as a shared library:

// Now we use the generic helper definitions above to define OPENRTI_API and OPENRTI_LOCAL.
// OPENRTI_LOCAL is used for non-api symbols.

#ifdef OPENRTI_DLL // defined if OPENRTI is compiled as a DLL
# define OPENRTI_LOCAL OPENRTI_HELPER_DLL_LOCAL
#else // OPENRTI_DLL is not defined: this means OPENRTI is a static lib.
# define OPENRTI_LOCAL
#endif // OPENRTI_DLL

And OPENRTI_HELPER_DLL_LOCAL is:

// Generic helper definitions for shared library support
#if defined _WIN32 || defined __CYGWIN__
# define OPENRTI_HELPER_DLL_LOCAL
#elif defined __GNUC__ && (4 <= __GNUC__)
# define OPENRTI_HELPER_DLL_LOCAL __attribute__ ((visibility("hidden")))
#elif defined __SUNPRO_C && (0x550 <= __SUNPRO_C)
# define OPENRTI_HELPER_DLL_LOCAL __hidden
#else
# define OPENRTI_HELPER_DLL_LOCAL
#endif


Related Topics



Leave a reply



Submit