[C++] Template in der Template Spezialisierung

Gast #5527224
Lesenswert?

Guten Morgen,

ich programmiere aktuell ein Programm in C++ mit Qt in Visual Studio bei 
dem ich eine .mat Datei (Matlab) einlesen möchte. Dafür nutze ich matio 
und dies funktioniert soweit super. Damit das Lesen/Schreiben einer .mat 
ein wenig übersichtlicher wird, baue ich aktuell einen kleinen Wrapper. 
(Keine Sorge, Fehlerhandling und Co. kommt noch.)

Für das Lesen der einfachen Datentypen habe ich mir folgendes Template 
gebastelt, welches auch super funktioniert:
1
template <typename T>
2
T get(QString name)
3
{
4
  T val;
5
  matvar_t* var = Mat_VarRead(matfp, name.toStdString().c_str());
6

7
  if (var->data_size == sizeof(T) && var != NULL)
8
    memcpy(&val, var->data, var->data_size);
9

10
  Mat_VarFree(var);
11

12
  return val;
13
}

Für das Lesen anderer Datentypen habe ich mir eine Spezialisierung 
erstellt, wie z.B. für Complex:
1
typedef std::complex<double> Complex; // In globals.h
2

3
template <>
4
Complex get<Complex>(QString name)
5
{
6
  double re, im;
7
  mat_complex_split_t c = { &re, &im };
8
  matvar_t* var = Mat_VarRead(matfp, name.toStdString().c_str());
9
  Mat_VarReadDataLinear(matfp, var, &c, 0, 1, 1);
10
  Mat_VarFree(var);
11

12
  return Complex(re, im);
13
}

Nun habe ich aber einen Datentyp der selbst ein Template besitzt. Wie 
kann ich eine Art Spezialisierung mit Template erstellen? Da hört man 
Template-Wissen bereits auf :( Als Beispiel mit std::vector<float>:
1
template <>
2
vector<float> get<vector<float>>(QString name)
3
{
4
  matvar_t* var = Mat_VarRead(matfp, name.toStdString().c_str());
5

6
  vector<float> val;
7
  val.resize(var->nbytes / var->data_size);
8
  memcpy(val.data(), var->data, var->nbytes);
9
  Mat_VarFree(var);
10

11
  return val;
12
}
13

14
vector<float> val = mat.get<vector<float>>("Variable");

Funktioniert super, nur ich müsste eine Spezialisierung für alle 
Datentypen erstellen, welche an std::vector übergeben werden können. Wie 
muss das Template aussehen für folgende Spezialisierung?
1
// Funktioniert natürlich nicht, soll das Problem verdeutlichen
2
template <> template <typename T> 
3
vector<T> get<vector<T>>(QString name)
4
{
5
  matvar_t* var = Mat_VarRead(matfp, name.toStdString().c_str());
6

7
  vector<T> val;
8
  val.resize(var->nbytes / var->data_size);
9
  memcpy(val.data(), var->data, var->nbytes);
10
  Mat_VarFree(var);
11

12
  return val;
13
}
Gast #5527334
Lesenswert?

Ich lasse den Kram in den Funktionen und den QString weg, da es 
irrelevant für dein Problem ist.

Du hast das Template:
1
template <typename T>
2
T get() {};

und kannst es vollständig speizalisieren für z.b. T=double:
1
template <>
2
double get() {};
3
auto wert = get<double>();

oder T=std::vector<double>
1
template <>
2
std::vector<double> get() {};
3
auto wert = get<std::vector<double>>();

was du nicht machen kannst, aber gerne machen würdest, ist das Template 
teilweise spezialisieren für
1
template <typename T>
2
std::vector<T> double get() {};
3
auto wert = get<std::vector<double>>();

Es können nur Klassetemplates teilweise spezialisiert werden, aber keine 
Funktionstamplates.
Deswegen musst du etwas anders vorgehen und aus deiner Funktion eine 
Klasse machen.
1
template <typename T>
2
struct Getter {
3
  static T get() {};
4
};

vollständig spezialisiert
1
template <>
2
struct Getter<double> {
3
  static double get() {};
4
};
5
auto wert = Getter<double>::get();

teilweise spezialisiert
1
template <typename T>
2
struct Getter<std::vector<T>> {
3
  static std::vector<T> get() {};
4
};
5
auto wert = Getter<std::vector<double>>::get();
#5527426
Lesenswert?

Ein wenig SFINAE magic:
1
#include <string>
2
#include <complex>
3
#include <vector>
4
#include <type_traits>
5
#include <iostream>
6

7
// std::remove_cvref still not in gcc???
8
namespace std {
9

10
template<typename T>
11
struct remove_cvref {
12
  using type = remove_cv_t<remove_reference_t<T>>;
13
};
14

15
template<typename T>
16
using remove_cvref_t = typename remove_cvref<T>::type;
17

18
}
19

20

21
// Check if type is specialization of some template
22
namespace detail {
23

24
template<typename, template<typename...> typename>
25
struct is_specialization_of_impl : std::false_type {};
26

27
template<template<typename...> typename T, typename... Ts>
28
struct is_specialization_of_impl<T<Ts...>, T> : std::true_type {};
29

30
}  
31

32
template<typename T, template<typename...> typename... Ts>
33
using is_specialization_of = std::disjunction<
34
    detail::is_specialization_of_impl<std::remove_cvref_t<T>, Ts>...>;
35

36
template<typename T, template<typename...> typename... Ts>
37
constexpr bool is_specialization_of_v{is_specialization_of<T, Ts...>::value};
38

39

40
// Get for non vector types
41
template <typename T>
42
std::enable_if_t<!is_specialization_of_v<T, std::vector>, T> get(std::string name)
43
{
44
  std::cout << __PRETTY_FUNCTION__ << "\n";
45
  std::cout << "get for non vector types\n";
46
  return {};
47
}
48

49
// Get for vector types
50
template<typename T>
51
std::enable_if_t<is_specialization_of_v<T, std::vector>, T> get(std::string name)
52
{
53
  std::cout << __PRETTY_FUNCTION__ << "\n";
54
  std::cout << "get for vector types\n";  
55
  return {};
56
}
57

58
using Complex = std::complex<double>;
59

60
// Get for complex type
61
template <>
62
Complex get<Complex>(std::string name)
63
{
64
  std::cout << __PRETTY_FUNCTION__ << "\n";
65
  std::cout << "get for complex types\n";
66
  return Complex{0, 0};
67
}
68

69

70
int main()
71
{
72
  std::cout << "\n\nrunning main\n";
73
  auto _1 = get<int>("_1");
74
  auto _2 = get<std::vector<int>>("_2");
75
  auto _3 = get<Complex>("_3");
76
}


http://coliru.stacked-crooked.com/a/125c1c1960e72f62
Gast #5527564
Lesenswert?

Hallo ihr zwei,

danke für die gezeigten Möglichkeiten. Die Variante von Vincent geht 
weit über mein Template-Wissen hinaus. Muss ich mal nachvollziehen, da 
gibt es wieder was zum Lernen.

Die Variante von mh lässt sich bei mir wahrscheinlich am einfachsten 
umsetzen. Ein Versuch ist es auf jeden Fall wert.

Danke!!!
#5528526
Lesenswert?

Gute Frage und gute Herangehensweise. Kleiner Tip noch:

mr. mo schrieb:
> if (var->data_size == sizeof(T) && var != NULL)

Der rechte Teil der Abfrage ist so wie er da steht etwas sinnlos, denn 
wenn er falsch ist wird er nie erreicht; ist der Pointer Null wird schon 
das dereferenzieren im linken Teil mit einer Segmentation Violation 
abbrechen. So wäre es richtig:
1
if ( var != NULL && var->data_size == sizeof(T) )
2
  ...

oder auch:
1
if ( var && var->data_size == sizeof(T) )
2
  ...

Antwort schreiben

Bitte melde dich an, um einen Beitrag zu schreiben.

oder

Mit Google-Account einloggen

Die Registrierung ist kostenlos und dauert nur eine Minute.

Jetzt registrieren