Forum: PC-Programmierung [C++] Template in der Template Spezialisierung


von mr. mo (Gast)


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
}

von mh (Gast)


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();

von Vincent H. (vinci)


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

von mr. mo (Gast)


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!!!

von Torsten R. (Firma: Torrox.de) (torstenrobitzki)


Lesenswert?

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

Du kannst die Klasse auch hinter einer Funktion verstecken, dann behälts 
Du das bisherige Interface bei.

von Niklas G. (erlkoenig) Benutzerseite


Lesenswert?

Oder mit Tag-Parametern:
1
template <typename T> struct Tag {};
2
3
// Allgemein
4
template <typename T> get2 (Tag<T>) {
5
}
6
7
// Speziell
8
Complex get2 (Tag<Complex>) {
9
}
10
11
// Partiell
12
template <typename T>
13
std::vector<T> get2(Tag<std::vector<T>>) {
14
}
15
16
// API
17
template <typename T>
18
decltype(auto) get () {
19
  return get2 (Tag<T> {});
20
}

von A. H. (ah8)


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
  ...

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.