c++templatesmacros

How to convert a template type into a new type


In the following template function f(), I want to convert the template type T to type TD.

For example, when T=A, the conversion results in type AD. For example, when T=B, the conversion results in type BD.

A,AD,B,BD are some C++ classes.

#include <iostream>

#define CONN(T, data) new T##D(data)

class A {
 private:
  int m_data;

 public:
  A(int data) : m_data(data) {}
  ~A() {}
  void print() { std::cout << "A.data = " << m_data << std::endl; }
};

class AD {
 private:
  int m_data;

 public:
  AD(int data) : m_data(data) {}
  ~AD() {}
  void print() { std::cout << "AD.data = " << m_data << std::endl; }
};

class B {
 private:
  int m_data;

 public:
  B(int data) : m_data(data) {}
  ~B() {}
  void print() { std::cout << "B.data = " << m_data << std::endl; }
};

class BD {
 private:
  int m_data;

 public:
  BD(int data) : m_data(data) {}
  ~BD() {}
  void print() { std::cout << "BD.data = " << m_data << std::endl; }
};

template <typename T>
void f() {
  auto ad = CONN(A, 100); // OK
  ad->print();
  auto bd = CONN(T, 100); // FAIL
  bd->print();
}

int main(int argc, char const* argv[]) {
  f<B>();
  return 0;
}
$ g++ 18.cpp -o t
18.cpp: In function 'void f()':
18.cpp:49:18: error: expected type-specifier before 'TD'
   49 |   auto bd = CONN(T, 100);
      |                  ^
18.cpp:3:27: note: in definition of macro 'CONN'
    3 | #define CONN(T, data) new T##D(data)
      |                           ^

When using macro function conversion, if the macro parameter is a specific type (e.g., A), the conversion can succeed (A-->AD). If the macro parameter is a template type (e.g., T), the conversion fails (T-->TD). But I don't have the specific class TD. I know that macro replacement is in the preprocessing stage, and the specific type of the template parameter cannot be known until the compile time. Therefore, the latter conversion will fail.

In the template parameter, is it possible to convert one type into another new type?


Solution

  • You can write a trait.

    template <typename>
    struct conn;
    
    template <>
    struct conn<A> {
        using type = AD;
    };
    
    template <>
    struct conn<B> {
        using type = BD;
    };
    
    template <typename T>
    using conn_t = typename conn<T>::type;
    

    Which you can then use

    template <typename T>
    void f() {
      conn_t<A> ad(100); // OK
      ad.print();
      conn_t<T> bd(100); // OK
      bd.print();
    }
    
    int main(int argc, char const* argv[]) {
      f<B>();
      return 0;
    }
    

    See it on coliru