Where and when should we use compile-time macro concatenation?
I saw this piece of code which I thought was great because it saved me from rewriting the getter member functions.
#define GET(n_,N_) \
template<typename T> \
const T &get##N_() const \
{ \
return n_; \
} \
From what I know, macros "write" code at compile time, so they introduce a function template inside each class, and since it is a template, it can compensate for any type. So I ended up with this:
class Foo
{
int m_a;
GET(m_a,A)
};
then I used like this:
std::cout<< foo->getA<int>() <<std::endl;
My question is WHEN and WHERE should I use macro templates? And is there a way to NOT specify the type when you call the getA member function? Is it because it's in a different namespace?
source to share
I'm going to assume that mixing macros and templates will result in you picking up the weaknesses of both, as you may have noticed. The return type of a function template can never be inferred, so you will always need to specify it. BUT luckily there is a solution, and it involves writing a type in your macro:
#define GETTABLE_ATTR(type, name) \
private:
type name_; \
public:
type get_##name() const \
{ \
return name_; \
}
If you use this macro, the good idea is still subjective - remember that you only write the code once, and your best bet is to write it in a way that will prevent mistakes while writing them. And will keep the code simple.
source to share
As with c++14
, there is still a need to use c macros.
I have two very commonly used macros combined with MTP to get a compile time constant that tells me if a method or attribute exists in the class. It just needs the name of the function, which cannot be given as a parameter to the template. So I prepared this inside a c macro that "writes" my template, which can then be used inside the enable_if clause.
I personally don't like your idea of ββ"automatic getters", but that's just a matter of taste.
As always in programming: if it helps, it is not "undefined behavior", is well documented in the code and cannot be done with a simpler movement since the use of c macros is allowed. For me, macros are a kind of "self-defense" for non-integrated language functions.
Another popular example is an enumeration with associated text for some kind of reflection or for serialization.
An example to catch the existence of a method:
#define DECLARE_HAS_METHOD_FULL_SIGNATURE(NAME) \
template<typename, typename T>\
struct has_method_full_signature_ ## NAME {\
static_assert(\
std::integral_constant<T, false>::value,\
"Second template parameter needs to be of function type.");\
};\
\
\
template<typename C, typename Ret, typename... Args>\
struct has_method_full_signature_ ## NAME <C, Ret(Args...)> {\
template<typename T>\
static constexpr auto check(T*)\
-> typename\
std::is_same<\
decltype( std::declval<T>(). NAME ( std::declval<Args>()... ) ),\
Ret \
>::type; \
\
template<typename>\
static constexpr std::false_type check(...);\
\
using type = decltype(check<C>(0)); \
static constexpr bool value = type::value;\
}
EDIT: Add some example code on how to use this c-macro stuff here.
#include <utility>
#include <iostream>
#include "component/mytypes_traits.h"
DECLARE_HAS_METHOD_PARMS_ONLY(funny);
DECLARE_HAS_METHOD_FULL_SIGNATURE(f1);
DECLARE_HAS_METHOD_FULL_SIGNATURE(f2);
DECLARE_HAS_METHOD_FULL_SIGNATURE(f3);
class A { public: void funny() {} };
class B { public: void dummy() {} };
class C
{
public:
int f1(int) { return 1;}
float f2(int,int) {return 2.0;}
int f3() { return 1;}
};
int main()
{
std::cout << has_method_parms_only_funny<A>::value << std::endl;
std::cout << has_method_parms_only_funny<B>::value << std::endl;
std::cout << "--" << std::endl;
std::cout << has_method_full_signature_f1< C, int()>::value << std::endl;
std::cout << has_method_full_signature_f1< C, int(int)>::value << std::endl;
std::cout << has_method_full_signature_f1< C, int(int,int)>::value << std::endl;
std::cout << "--" << std::endl;
std::cout << has_method_full_signature_f2< C, float()>::value << std::endl;
std::cout << has_method_full_signature_f2< C, float(int)>::value << std::endl;
std::cout << has_method_full_signature_f2< C, float(int,int)>::value << std::endl;
std::cout << "--" << std::endl;
std::cout << has_method_full_signature_f3< C, int()>::value << std::endl;
std::cout << has_method_full_signature_f3< C, int(int)>::value << std::endl;
std::cout << has_method_full_signature_f3< C, int(int,int)>::value << std::endl;
std::cout << "--" << std::endl;
}
source to share