[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

Hinweis: der ursprüngliche Beitrag ist mehr als 6 Monate alt. Bitte hier nur auf die ursprüngliche Frage antworten, für neue Fragen einen neuen Beitrag erstellen.

Formatierung
  • [c]C-Code[/c]
  • [code]Code in anderen Sprachen, ASCII-Zeichnungen[/code]
  • [math]Formel in LaTeX-Syntax[/math]
  • [[Titel]] – Link zu einem Artikel
  • Verweis auf einen anderen Beitrag: Rechtsklick auf dessen Beitrags-ID (#8078628), „Adresse kopieren“, in den Text einfügen

Mehr zur Formatierung

Längeren Sourcecode als Anhang, nicht in den Text. JPG nur für Fotos und Scans; Zeichnungen und Screenshots als PNG oder GIF. Siehe Bildformate.

Schon registriert? Anmelden

Wird nicht angezeigt. Wir schicken dir einen Link; sobald du damit bestätigst, erscheint dein Beitrag.

Mit dem Abschicken legst du einen kostenlosen Account an und erkennst die Nutzungsbedingungen und die Datenschutzerklärung an.