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?
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.
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;
}