Forum: PC-Programmierung C++, Einen Methoden-Dispatcher zur Kompilezeit erzeugen?


von CppBert (Gast)


Lesenswert?

Sorry für den Haufen Quelltext - aber ich wollte ein Beispiel machen das 
auch getestet werden kann - weniger ist sehr schwierig, ohne tausend 
Fragen offen zu lassen.

Und Nein, es ist keine Hausaufgabe :)

zum Online kompilieren/ausführen: https://onlinegdb.com/SyMiIheEN
1
#include <cassert>
2
#include <tuple>
3
#include <vector>
4
5
// Ich brauche ein Template (oder Tips) wie ich von einer Dispatcher-Klasse aus
6
// ohne viel Boilerplate-Code native Methoden aufrufen kann
7
// Das ganze Konzept funktioniert (schon etwas länger) wie es soll 
8
// aber ich würde sehr gerne den Boilerplate Code reduzieren 
9
10
// Hier ein stark vereinfachtes Beispiel - es geht mir in keinster Weise
11
// um das Grundkonzept (das erkennt man in diesem Beispiel nicht wirklich) 
12
// sonder nur um ein bisschen Template-Magic in der
13
// itest_class::test2_waere_perfekt Methode - der Rest-Code
14
// ist nur damit meine Frage nicht total abstrakt ist
15
// und man auch was ausführen kann
16
17
// mit up-to-date: VS2017 15.9.6 oder gcc 8.2, also (fast) alles was C++17 so hergibt
18
19
struct i_value // Value-Schnittstelle fuer Konstanten,Expression,Referenzen,...
20
{
21
  // INFO: Mein Framework stellt per Reflection-Prinzip magisch sicher das nur die selben Typen verbunden werden (void* ist bei mir 100% safe)
22
  virtual void set(const void* const value) = 0;
23
  virtual void get(void* const value) = 0;
24
};
25
26
// hier in diesem Beispiel reduziere ich das ganze auf "Variablen"
27
// also Objekte die das i_value Interface implementierung und einen Value-Zustand haben
28
template<typename ValueType>
29
struct value_t : i_value
30
{
31
  ValueType _value = ValueType();
32
  value_t(const ValueType& value) :_value(value) {}
33
34
  // hier ist normalerweise noch Absicherungs-Code drinn/dahinter/drumherum
35
  const ValueType& magical_safe_cast(const void* const value) const
36
  {
37
    return *reinterpret_cast<const ValueType*>(value);
38
  }
39
  ValueType& magical_safe_cast(void* value)
40
  {
41
    return const_cast<ValueType&>(static_cast<const value_t*>(this)->magical_safe_cast(value));
42
  }
43
44
  void set(const void* const value) override
45
  {
46
    set(magical_safe_cast(value));
47
  }
48
49
  void get(void* value) override
50
  {
51
    magical_safe_cast(value) = get();
52
  }
53
54
  void set(const ValueType& value)
55
  {
56
    _value = value;
57
  }
58
59
  ValueType& get()
60
  {
61
    return _value;
62
  }
63
};
64
65
// liefert den return-type einer Methode
66
template <typename Return, typename Class, typename ... Parameter>
67
Return get_return_type(Return(Class::*)(Parameter...));
68
69
// liefert die parameter-typen einer Methode
70
template <typename Class, typename Return, typename... Parameter>
71
std::tuple<Parameter...> method_args(Return(Class::*)(Parameter...))
72
{
73
  return std::tuple<Parameter...>();
74
}
75
76
// kleine Abstraktion um in, out und inout Verhalten zu erzeugen
77
template<typename ValueType>
78
struct in_parameter_t
79
{
80
  ValueType _native_value = ValueType();
81
  i_value* _value = nullptr;
82
  in_parameter_t(i_value* value) :_value(value) {}
83
84
  void load()
85
  {
86
    _value->get(&_native_value);
87
  }
88
89
  void store() {} // nicht implementiert da IN
90
91
  ValueType& operator()()
92
  {
93
    return _native_value;
94
  }
95
};
96
97
template<typename ValueType>
98
struct out_parameter_t
99
{
100
  ValueType _native_value = ValueType();
101
  i_value* _value = nullptr;
102
  out_parameter_t(i_value* value) :_value(value) {}
103
104
  void load() {} // nicht implementiert da OUT
105
106
  void store()
107
  {
108
    _value->set(&_native_value);
109
  }
110
111
  ValueType& operator()()
112
  {
113
    return _native_value;
114
  }
115
};
116
117
template<typename ValueType>
118
struct inout_parameter_t
119
{
120
  ValueType _native_value = ValueType();
121
  i_value* _value = nullptr;
122
  inout_parameter_t(i_value* value) :_value(value) {}
123
124
  void load()
125
  {
126
    _value->get(&_native_value);
127
  }
128
129
  void store()
130
  {
131
    _value->set(&_native_value);
132
  }
133
134
  ValueType& operator()()
135
  {
136
    return _native_value;
137
  }
138
};
139
140
// traits um aus den Methoden/Result-Typen den richtigen Adapter zu bekommen
141
142
template<typename ValueType>
143
struct parameter_type_t {};
144
145
template<>
146
struct parameter_type_t<const int&>
147
{
148
  using type = in_parameter_t<int>;
149
};
150
151
template<>
152
struct parameter_type_t<const int>
153
{
154
  using type = in_parameter_t<int>;
155
};
156
157
template<>
158
struct parameter_type_t<const double&>
159
{
160
  using type = in_parameter_t<double>;
161
};
162
163
template<>
164
struct parameter_type_t<const double>
165
{
166
  using type = in_parameter_t<double>;
167
};
168
169
template<>
170
struct parameter_type_t<int&>
171
{
172
  using type = inout_parameter_t<int>;
173
};
174
175
template<>
176
struct parameter_type_t<double&>
177
{
178
  using type = inout_parameter_t<double>;
179
};
180
181
// "native" classe von der ich Methoden per ueber die itest_class aufrufen will
182
struct test_class
183
{
184
  int value = 100;
185
186
  void test0() { return; }
187
  void test1(const int& x, const double& y) { return; }
188
  double test2(const int& x, const double& y, double& z)
189
  {
190
    double res = x + y;
191
    z = 10;
192
    return res;
193
  }
194
  int& test3() { return value; }
195
};
196
197
using iparameter = std::vector<i_value*>;
198
199
// "native" class Wrapper/Dispatcher
200
// mein Framework erlaubt magisch-dynamisch das Aufrufen dieser Dispatch-Funktionen (mit ivalue Parametern)
201
202
struct itest_class
203
{
204
  test_class object; // liegt normalerweise wo anders
205
206
  // Stufe 0: alles von Hand, maximal unsicher (auch wenn ich mit Tricks hier Fehler erkenne)
207
  void test2_von_hand(const iparameter& parameter, i_value* const result)
208
  {
209
    assert(parameter.size() == 3);
210
211
    int x = 0;
212
    double y = 0.0;
213
    double z = 0.0;
214
    double res = 0.0;
215
216
    // load
217
    parameter[0]->get(&x); // IN
218
    parameter[1]->get(&y); // IN
219
    parameter[2]->get(&z); // INOUT
220
221
    // run
222
    res = object.test2(x, y, z);
223
224
    // store
225
    result->set(&res);
226
    //parameter[0]->set(&x); // nicht noetig da IN parameter
227
    //parameter[1]->set(&y); // nicht noetig da IN parameter
228
    parameter[2]->set(&z);
229
  }
230
231
  // hier habe ich versucht micht reflektiv an der nativen-Methode zu orientieren
232
  // weniger Fehler, aber zu viel Code - nur eine Blaupause fuer die wohl notwendigen
233
  // Template-Helper
234
  void test2_bisschen_besser(const iparameter& parameter, i_value* const result)
235
  {
236
    // die Methoden-Signatur abgreifen
237
    using native_parameter_types = decltype(method_args(&test_class::test2));
238
    using native_return_type = decltype(get_return_type(&test_class::test2));
239
    static_assert(!std::is_same<native_return_type, void>::value, "void fehlt noch");
240
241
    assert(parameter.size() == std::tuple_size<native_parameter_types>::value);
242
243
    // diesen tuple muesste man doch zur kompilezeit generieren koennen, oder?
244
    using parameter_types_t = std::tuple
245
      <
246
      parameter_type_t<std::tuple_element<0, native_parameter_types>::type>::type,
247
      parameter_type_t<std::tuple_element<1, native_parameter_types>::type>::type,
248
      parameter_type_t<std::tuple_element<2, native_parameter_types>::type>::type
249
      >;
250
251
    // diese instanzen vielleicht in einem tupel halten - aber wie geht das?
252
    std::tuple_element<0, parameter_types_t>::type p0(parameter[0]);
253
    std::tuple_element<1, parameter_types_t>::type p1(parameter[1]);
254
    std::tuple_element<2, parameter_types_t>::type p2(parameter[2]);
255
256
    using res_t = out_parameter_t<native_return_type>;
257
    res_t res(result);
258
259
    // load - schoen homogen, kann man das zur kompilezeit expandieren?
260
    p0.load();
261
    p1.load();
262
    p2.load();
263
264
    // run
265
    // https://cpppatterns.com/patterns/apply-tuple-to-function.html // laut diesem tutorial kann man methoden auch mit tupels aufrufen
266
    res() = object.test2(p0(), p1(), p2());
267
    // wie koennte man native_return_type == void loesen? if constexpr?
268
269
    // store
270
    res.store(); // setzt was
271
272
    // schoen homogen, kann man das zur kompilezeit expandieren?
273
    p0.store(); // macht nichts
274
    p1.store(); // macht nichts
275
    p2.store(); // setzt was
276
  }
277
278
  void test2_waere_perfekt(const iparameter& parameter, i_value* const result)
279
  {
280
    //!!!
281
    //Ziel meine Frage: Wie kann ich so ein run_method Template implementieren um
282
    //mir den ganzen Boilerplate Code zu sparen
283
284
    // mein Gefühl sagt mir das so ein Template moeglich sein muesste
285
    //run_method(&object, &test_class::test2, parameter, result);
286
  }
287
};
288
289
290
int main()
291
{
292
  // Dispatcher-Klasse instanzieren
293
  itest_class itest_object;
294
295
  value_t<int> x(11); // value by ref
296
  value_t<double> y(22.0); // value by ref
297
  value_t<double> z(44.0); // ref
298
  iparameter parameter{ &x, &y, &z };
299
300
  value_t<double> result(0);
301
302
  result.set(0.0);
303
  z.set(0.0);
304
  itest_object.test2_von_hand(parameter, &result);
305
  assert(result.get() == 33.0);
306
  assert(z.get() == 10.0);
307
308
  result.set(0.0);
309
  z.set(0.0);
310
  itest_object.test2_bisschen_besser(parameter, &result);
311
  assert(result.get() == 33.0);
312
  assert(z.get() == 10.0);
313
314
  //result.set(0.0);
315
  //z.set(0.0);
316
  //itest_object.test2_waere_perfekt(parameter, &result);
317
  //assert(result.get() == 33.0);
318
  //assert(z.get() == 10.0);
319
320
  return 0;
321
}

von 900ss (900ss)


Lesenswert?

Und weshalb hängst du deinen Sourcecode nicht einfach als Datei an dein 
Posting? Das kann man auch sehr leserlich sogar mit Syntax Highlight 
hier ansehen. Und dein Posting wäre nicht kilometer lang.

von CppBert (Gast)


Angehängte Dateien:

Lesenswert?

Als Anhang - ging nicht im Chrome, aber im Firefox klappt es?

von CppBert (Gast)


Lesenswert?

Noch ein bisschen weiter probiert

jetzt versuche ich
1
  void test2_bisschen_besser(const iparameter& parameter, i_value* const result)
2
  {
3
    // die Methoden-Signatur abgreifen
4
    using native_parameter_types = decltype(method_args(&test_class::test2));
5
    using native_return_type = decltype(get_return_type(&test_class::test2));
6
    static_assert(!std::is_same<native_return_type, void>::value, "void fehlt noch");
7
8
    assert(parameter.size() == std::tuple_size<native_parameter_types>::value);
9
10
    // diesen tuple muesste man doch zur kompilezeit generieren koennen, oder?
11
    using parameter_types_t = std::tuple
12
      <
13
      parameter_type_t<std::tuple_element<0, native_parameter_types>::type>::type,
14
      parameter_type_t<std::tuple_element<1, native_parameter_types>::type>::type,
15
      parameter_type_t<std::tuple_element<2, native_parameter_types>::type>::type
16
      >;
17
18
    auto p = parameter_types_t
19
    (
20
      parameter[0], 
21
      parameter[1], 
22
      parameter[2]
23
    );
24
25
    // load - schoen homogen, kann man das zur kompilezeit expandieren?
26
    std::get<0>(p).load();
27
    std::get<1>(p).load();
28
    std::get<2>(p).load();
29
30
    using res_t = out_parameter_t<native_return_type>;
31
    res_t res(result);
32
33
    // https://cpppatterns.com/patterns/apply-tuple-to-function.html // laut diesem tutorial kann man methoden auch mit tupels aufrufen
34
    res() = object.test2(
35
      std::get<0>(p)(),
36
      std::get<1>(p)(),
37
      std::get<2>(p)()
38
    );
39
40
    // store
41
    res.store(); // setzt was
42
43
    // schoen homogen, kann man das zur kompilezeit expandieren?
44
    std::get<0>(p).store(); // macht nichts
45
    std::get<1>(p).store(); // macht nichts
46
    std::get<2>(p).store(); // setzt was
47
  }

von M.K. B. (mkbit)


Lesenswert?

Dunkle Templatemagie, juhu. :-D
Ich hab allerdings irgendwie die Vermutung, dass du mit Templates C++ 
Variablen nachbaust. Vielleicht kannst du mir erklären, wofür man das 
ganze verwenden kann. ich würde gerne verstehen, was du damit optimieren 
möchtest.

Ich konnte das folgende nicht ausprobieren, aber vielleicht hilft das. 
Du kannst ja deinen Fortschritt posten, fände ich auf jeden Fall 
interessant.

// diesen tuple muesste man doch zur kompilezeit generieren koennen, 
oder?
Das müsste mit Variadic Templates gehen. 
https://stackoverflow.com/questions/17618938/how-to-make-a-tuple-from-a-tuple/17619059?noredirect=1#17619059

// diese instanzen vielleicht in einem tupel halten - aber wie geht das?
Warum machst du das load nicht beim Funktionsaufruf, dann brauchst du 
das Tuple nicht.

// load - schoen homogen, kann man das zur kompilezeit expandieren?
Vielleicht über rekursive Templates, aber hier würde ich auf 
Loopunrolling des Compilers setzen und das mit einer Schleife machen.

von CppBert (Gast)


Lesenswert?

>Dunkle Templatemagie, juhu. :-D

gar nicht soo dunkel

>Ich hab allerdings irgendwie die Vermutung, dass du mit Templates C++
>Variablen nachbaust. Vielleicht kannst du mir erklären, wofür man das
>ganze verwenden kann. ich würde gerne verstehen, was du damit optimieren
>möchtest.

Kurz: Ich habe ein Framework welches erlaubt in einem UI - Objekte(mit 
Methoden), Expressions, usw. zu erzeugen
und diese mit einandern zu verschränken - so eine Art Programmiersystem

Konzept: du schreibst eine normale Klasse mit Members und Methoden und 
schreibst
dazu eine Klasse welche die Fähigkeiten der normalen Klasse so 
beschreibt das
du sie frei wählen, instanziern und Methoden aufrufen kannst - das geht 
alles

aber der Code um von der Abstrakten Schnittstelle in die echte 
Implementierung
zu kommen ist einfach ein wenig umfangreich

aber mit Templates laesst sich das definitiv in einen Einzeiler 
verwandeln - ich
bin nur einfach nicht so fit mit den C++11,14,17 Variadic 
Templates/Folding expressions usw
und tu mich da schwer

mein jetztiges Beispiel enthält in der test2_von_hand-Methode den 
jetzigen Code
für so einen Abstrakte-Schnittstelle->native-Schnittstelle adaption

die test2_bisschen_besser-Methode extrahiert z.B. schon direkt aus der 
nativen Methode
den Return-Type und die Parameter - jetzt fehlt nur noch etwas code der 
die
homogenen Teile zur Kompilezeit expandiert - schwupps muss ich gar 
keinen Adaptions-Code
mehr schreiben

von tictactoe (Gast)


Lesenswert?

Ich bin mir nicht sicher, ob das möglich ist. Die Anzahl der Parameter 
ist dynamisch (parameter.size()), die kannst du mit variadic Templates 
überhaupt nicht aufschließen, weil da die Anzahl ja zur Compile-Zeit 
bekannt sein muss. Aber die Parametertypen sind statisch 
(iparameter::value_type ist ein Pointer auf die Basisklasse), das stellt 
den Zweck von Templates in Frage.

Natürlich könnte ich aber mit meinem Verständnis vom Problem komplett 
auf dem Holzweg sein...

von Bert3 (Gast)


Lesenswert?

tictactoe schrieb:
> Ich bin mir nicht sicher, ob das möglich ist. Die Anzahl der Parameter
> ist dynamisch (parameter.size()), die kannst du mit variadic Templates
> überhaupt nicht aufschließen, weil da die Anzahl ja zur Compile-Zeit
> bekannt sein muss

Die orientierung ist die signatur der methode, die ist komplett zur 
kompilezeit verarbeitbar, der parameter vektor wird nur ein runtime 
parameter sein, nicht alles muss zur kompilezeit evaluiert werden 
koennen damit ich templates nutzen kann

von CppBert (Gast)


Angehängte Dateien:

Lesenswert?

Ich habe das Beispiel und die Fragen noch mal drastisch gekürzt - so wie 
ich es auch im CppForum gepostet habe

Online Kompilieren/Ausführen: https://onlinegdb.com/Skk-I3WNN
1
// aktuelle VS2017 Version, oder GCC 8.2, C++14 oder C++17 waere moeglich
2
3
#include <tuple>
4
#include <vector>
5
#include <cassert>
6
7
template<typename Value>
8
struct in_parameter_t
9
{
10
    in_parameter_t(void* value) {}
11
};
12
13
template<typename Value>
14
struct inout_parameter_t
15
{
16
    inout_parameter_t(void* value) {}
17
};
18
19
template<typename ValueType>
20
struct parameter_type_t {};
21
22
template<>
23
struct parameter_type_t<int>
24
{
25
    using type = in_parameter_t<int>;
26
};
27
28
template<>
29
struct parameter_type_t<int&>
30
{
31
    using type = inout_parameter_t<int>;
32
};
33
34
using parameter_t = std::vector<void*>;
35
36
int main()
37
{
38
    //------
39
    // Kompilezeit-Parameter
40
    // Methoden-Parameter-Typen
41
    using native_parameter_types = std::tuple<int,int&>;
42
    //------
43
    // Laufzeit-Parameter
44
    int p0 = 0;
45
    int p1 = 0;
46
    parameter_t parameter{&p0, &p1}; // muss ein std::vector sein (std::array ist zu fix)
47
    // zur Laufzeit garantiert das zu jedem native_parameter_types[x] ein parameter[x] existiert
48
    assert(parameter.size() == std::tuple_size<native_parameter_types>::value);
49
    //------
50
    
51
    // hier kommen mein Fragen
52
53
    // aus dem nativen typ einen adapter typ erzeugen
54
    using parameter_types_t = std::tuple
55
                              <
56
                              parameter_type_t<std::tuple_element<0, native_parameter_types>::type>::type,
57
                              parameter_type_t<std::tuple_element<1, native_parameter_types>::type>::type
58
                              >;
59
    //====> FRAGE 1: geht das auch mit einer Template-Funktion?
60
    //so in der Art?
61
    //using parameter_types_t = get_parameter_types<native_parameter_types>::types;
62
63
    // die adapter mit dem parameter-pointer instanzieren
64
    auto p = parameter_types_t
65
             (
66
                 parameter[0],
67
                 parameter[1]
68
             );
69
    //====> FRAGE 2: geht das auch mit einer Template-Funktion?
70
    //so in der Art?
71
    //auto p = get_parameter<parameter_types_t>(parameter);
72
73
    return 0;
74
}

von tictactoe (Gast)


Lesenswert?

Ok, verstanden. Ja, das müsste mit variadic Templates gehen, und zwar 
mit std::index_sequence.

Da wird eine Hilfsfunktion benötigt, die einen Parameter 
std::index_sequence<I...> hat. Das Argument dazu ist 
std::index_sequence_for<sizeof...T> (wenn T ein Parameter-Pack ist) oder 
in deinem Fall wohl eher 
make_index_sequence<tuple_size<parameter_types_t>::value>{}.

In der Hilfs-Templatefunktion kann man dann z.B mit parameter[I]... auf 
einzelnen Werte im vector zugreifen. Oder den Hilfstyp konstruieren:
1
using parameter_types_t = std::tuple<parameter_type_t<std::tuple_element<I, native_parameter_types>::type>::type...>

Ich hab' das zuletzt hier gemacht:

https://github.com/j6t/cpptk/blob/0809f20ba028a87c58587c4024c5c8a910d2adcf/base/cpptkbase.h#L298-L310

aber nicht verwirren lassen: im dortigen Beispiel ist die get-Funktion 
nicht std::get, sondern eine Member-Template der Klasse Params.

von CppBert (Gast)


Lesenswert?

@tictactoe - so in etwa?
1
namespace detail
2
{
3
  template <typename ParameterTypesTuple_TP, size_t... Indice_TP>
4
  auto get_parameter(parameter_t& parameter,   std::index_sequence<Indice_TP...>)
5
  {
6
    return ParameterTypesTuple_TP(parameter[Indice_TP]...);
7
  }
8
}
9
10
template <typename ParameterTypesTuple_TP>
11
auto get_parameter(parameter_t& parameter)
12
{
13
  constexpr auto size = std::tuple_size<ParameterTypesTuple_TP>::value;
14
  return detail::get_parameter<ParameterTypesTuple_TP>(parameter, std::make_index_sequence<size>());
15
}

macht aus
1
auto p = parameter_types_t
2
(
3
  parameter[0],
4
  parameter[1]
5
);

diesen code
1
auto p = get_parameter<parameter_types_t>(parameter);

und es scheint auch zu funktionieren :) - super

aber das pure mapping von einem typ-tuple auf einen anderen macht mir 
noch probleme - ich weiss nicht wo ich die index_sequence 
hinstellen/initalisieren soll wenn ich doch nur einen Typ erzeuge (also 
keine helper-funktion sondern ein struct mit using types = xyz habe)

kannst du mir dafür noch ein kleines Beispiel "konkretisieren"
1
  using parameter_types_t = std::tuple
2
    <
3
    parameter_type_t<std::tuple_element<0, native_parameter_types>::type>::type,
4
    parameter_type_t<std::tuple_element<1, native_parameter_types>::type>::type
5
    >;

von CppBert (Gast)


Lesenswert?

irgendwie so denke ich
1
template<typename NativeParameterTypesTuple_TP>
2
struct get_parameter_types
3
{
4
  constexpr auto size = std::tuple_size<NativeParameterTypesTuple_TP>::value;
5
  //std::get<N>(std::forward<NativeParameterTypesTuple_TP>(t))...
6
  //using types = ...
7
};

ich komme einfach nicht drauf wo ich meine index_sequence einbauen muss
1
using parameter_types_t = std::tuple
2
<
3
  parameter_type_t<std::tuple_element<0, native_parameter_types>::type>::type,
4
  parameter_type_t<std::tuple_element<1, native_parameter_types>::type>::type
5
>;

von tictactoe (Gast)


Lesenswert?

Du baust dir eine Template-Funktion, die den gewünschten Typ 
zusammenbaut und als Rückgabewert hat, ähnlich wie get_parameter(); 
nennen wir sie get_parameter_types(). Dann brauchst du nur mehr sagen:
1
using parameter_types_t = decltype(get_parameter_types(native_parameter_types));

Normalerweise macht man solche Typtransformationen ja über 
Klassentemplates, aber hier brauchen wir irgendwo auf dem Weg 
Funktionen, damit der Compiler den Parameter-Typ 
std::index_sequence<Indice_TP...> ableitet.

von M.K. B. (mkbit)


Angehängte Dateien:

Lesenswert?

Das war mal wieder eine nette Aufgabe um wieder etwas Meta Template 
Programming zu üben. :-)

Im Anhang hab ich meine Version mit Lösung der Probleme angehängt. Am 
besten mit dem Original vergleichen und dann Fragen, wenn was unklar 
ist. Ich habe auch Dinge geändert, die kein Problem der Fragestellung 
waren, aber vielleicht ganz lehrreich sein könnten. Im Code sind auch 
Links zu Foren, die mich auf die Ideen gebracht habe. Wenn einer noch 
eine bessere Lösung hat, ich lerne gerne dazu.

Hier ein paar Anmerkungen zu meine Änderungen:
Seit C++14 kann man type und value kürzer schreiben.
1
std::tuple_element<0, native_parameter_types>::type
2
std::tuple_element_t<0, native_parameter_types>
3
4
std::is_same<native_return_type, void>::value
5
std::is_same_v<native_return_type, void>

Den parameter_type_t habe ich mal über enable if für arithmetische Typen 
automatisch spezialisieren lassen. Dann muss man das nicht für jeden 
Integertyp explizit machen.

Der Trick bei der Typeumwandlung ist, dass man nicht das Tuple als 
Templateparameter angibt. Damit werden die Typen im Tuple als Args 
genommen. Diese Parameter entpackt man und erstellt daraus ein neues 
Tuple.
1
template <typename... Args>
2
struct convert_to_param_type_t<std::tuple<Args...>>
3
{
4
     using type = std::tuple<typename parameter_type_t<Args>::type...>;
5
};

Das ausführen einer Funktion auf jedem Element des Tuples und das Laden 
aus dem Vektor funktioniert über eine Zwischenebene, die die Indizes für 
den Tuplezugriff erzeugt. In meinem Fall ist die Funktion explizit 
angegeben. Ich fände es interessant, ob man die Funktion auch von außen 
(z.B. als Lambda) mitgeben kann. Bei mir scheiterte es aber immer daran, 
dass diese ja für verschiedene Typen gültig sein muss.
1
template <typename... Args>
2
static void load_each(std::tuple<Args...>& t)
3
{
4
     // Tuple weiterleiten
5
     // Aus der Anzahl der Tupleelemente Compiletimeindizes erzeugen.
6
     // Man erzeugt hier eine Instanz einer integer_sequence, aber die Indizes sind im Typ und nicht als Member enthalten, also erzeugt dies keinen Code.
7
     load_each(t, std::index_sequence_for<Args...>{});
8
}
9
10
template <typename... Args, std::size_t... Idx>
11
static void load_each(std::tuple<Args...>& t, std::index_sequence<Idx...>)
12
{
13
    // Diese fold expression expandiert die Indizes, wichtig sind die äußeren Klammern.
14
    (std::get<Idx>(t).load(), ...);
15
    // Der Compiler macht daraus 
16
    // (std::get<0>(t).load(), std::get<1>(t).load(), std::get<2>(t).load())
17
}

von CppBert (Gast)


Lesenswert?

und wie expandiere ich meinen native_parameter_types-tuple damit ich mit 
den parameter-packs arbeiten kann?

Ich häng total

von CppBert (Gast)


Lesenswert?

irgendwie so?
1
namespace detail
2
{
3
  template <typename NativeTypesTuple_TP, size_t... Indice_TP>
4
  auto get_parameter_types(std::index_sequence<Indice_TP...>)
5
  {
6
    return std::tuple< typename parameter_type_t<typename std::tuple_element<Indice_TP, NativeTypesTuple_TP>::type >::type... >();
7
  }
8
}
9
10
template <typename NativeTypesTuple_TP>
11
using get_parameter_types = decltype(detail::get_parameter_types<NativeTypesTuple_TP>(std::make_index_sequence<std::tuple_size<NativeTypesTuple_TP>::value>()));

mein Problem ist das ich den nicht einfach default-Konstruiert 
zurueckliefern kann - meine tuple-elemente können nicht 
default-konstruiert werden
fällt dann die Lösung mit der template-Funktion weg?

von M.K. B. (mkbit)


Lesenswert?

CppBert schrieb:
> und wie expandiere ich meinen native_parameter_types-tuple damit ich mit
> den parameter-packs arbeiten kann?

Mein Code ist übrigens in VS 2017 compilierbar (andere nicht getestet) 
und funktioniert.

Ich geh mal davon aus, dass du die Typen expandieren willst. Das 
funktioniert anders, als wenn man mit den Werten arbeiten will.
Das Problem hatte ich erst auch, aber das macht man mit folgendem Trick. 
Ich zeigs mal an einem einfachen Beispiel.
1
template <typename... Args>
2
struct convert_to_param_type_t;
3
4
// Templatespezialiserung für tuple Typen.
5
// !!! Kein Tuple im Templateparameter Args
6
template <typename... Args>
7
struct convert_to_param_type_t<std::tuple<Args...>>
8
{
9
    using type = std::tuple<typename parameter_type_t<Args>::type...>;
10
};
11
12
using native_parameter_types = std::tuple<int, double>;
13
14
// Hier wird konvertiert:
15
using parameter_types_t = convert_to_param_type_t<native_parameter_types>::type;

Der Compiler will jetzt das Template convert_to_param_type_t 
instanzieren. Der übergeben Type ist eine Tuple, deswegen greift hier 
die Spezialisierung. Aus dem Tupletype werden die variadic 
Templateparameter deduced (Args = int, double).
Die Args werden entpackt und daraus ein neuer Type erzeugt.
1
// Das macht der Compiler.
2
using type = std::tuple<parameter_type_t<int>::type, parameter_type_t<double>::type>>;

von CppBert (Gast)


Lesenswert?

@M.K. B.
ja das funktioniert wie es soll :)

@tictactoe
Danke auch für deine Mühen - ohne decltype fühlt sich an dieser Stelle 
richtiger an

hab ihr noch eine Idee wie ich folgendes loesen könnte
1
template <size_t Index>
2
struct info_t{};
3
4
template <>
5
struct info_t<0>
6
{
7
  static constexpr auto name = "Erster";
8
};
9
  
10
template <>
11
struct info_t<1>
12
{
13
  static constexpr auto name = "Zweiter";
14
};
15
16
using infos = std::tuple
17
<
18
  info_t<0>,
19
  info_tr_t<1>
20
  //...
21
  //info_t<N>
22
>;
23
24
//hier braeuchte ich eine template welches alle infos von 0..N
25
// in den tuple steckt
26
template<size_t LastInfo>
27
using collect_infos = ... ???
28
29
using infos = collect_infos<2>;

eine Idee?

ansonsten ich meine Anfangs-Frage schon zu 100% beantwortet - alles 
funktioniert wie es soll

von CppBert (Gast)


Lesenswert?

noch als info wiso ich bei der collect_infos<N> sache nicht weiter 
komme:

ja ich denke ich brauche eine integer_sequence d.h. für mich das ich 
irgendeinen form von template-funktion brauche (eben für die sequence), 
aber am ende soll wieder nur ein Typ dabei rauskommen (kein value) also 
kein return sonder ein unsing infos_t = xyz ...

ich bin mir nie sicher wann ich mit sequencen, return und decltype 
arbeiten soll und wann ich gezwungen bin es völlig anders zu machen

von CppBert (Gast)


Lesenswert?

Habe es hin bekommen - ABER...

namespace detail {

template<size_t ... I>
auto collect_infos(std::index_sequence<I ...>)
{
  using infos= std::tuple<info_t<I>...>;
  return infos();
}

}

template<size_t Count>
using collect_infos = 
decltype(detail::collect_infos(std::make_index_sequence<Count>{}));

Fragen:
-wie kann ich die info_t durch einen template-Parameter ersetzen?

wenn ich anstatt info_t ein Info-Template parameter = info_t nutze 
kompilierts nicht wegen unvollstaendigem typ

-was würde ich machen wenn meine Tuple-Types sind default-Konstruierbar 
ist? also der decltype-Trick nicht funktioniert?

von M.K. B. (mkbit)


Lesenswert?

CppBert schrieb:
> ich bin mir nie sicher wann ich mit sequencen, return und decltype
> arbeiten soll und wann ich gezwungen bin es völlig anders zu machen

Mein Eindruck war bisher immer, dass man eine Sammlung von "Tricks" 
braucht und dann etwas Kreativität.

Wann benutzt man return?
Wann man Daten zurückgeben möchte.

Wann benutzt man using?
Wenn man Typdaten "zurückgeben" möchte.

Wann benutzt man decltype?
Wenn man den Typ eines Ausdrucks ermitteln möchte. Das kann hilfreich 
sein, wenn man z.B. überladene Funktionen mit verschiedenen 
Rückgabetypen hat. Jetzt könnten man sich dafür eine Template 
Lookuptabelle mit den Typen machen. Alternativ nutzt man decltype auf 
dem Funktionsaufruf. Je nach Fall kann das einfacher sein.

Wann benutzt man sequence?
Wenn man den Index braucht und nicht einfach nur eine Liste. In deinem 
Projekt brauchst du es einmal um im Vektor die richtigen Elemente zu 
lesen oder um die Info aus einer Indexbasierten Tabelle auszulesen. Für 
die Typkonvertierung braucht man es nicht, weil man es einfach nur auf 
jedem Element anmelden möchte und der Index nicht nötig ist.

Sonst braucht man außerdem häufig zwei Ebenen, weil man mit dem ersten 
Template irgendwas aus dem Typ ermittelt und das dann an die zweite 
Ebene weitergibt. Wie z.B. bei dem Tuple, wo man mit dem ersten Template 
die Typen in ein parameter pack umwandelt und das mit dem anderen 
Template konvertiert.

von M.K. B. (mkbit)


Lesenswert?

CppBert schrieb:
> Fragen:
> -wie kann ich die info_t durch einen template-Parameter ersetzen?
>
> wenn ich anstatt info_t ein Info-Template parameter = info_t nutze
> kompilierts nicht wegen unvollstaendigem typ
>
> -was würde ich machen wenn meine Tuple-Types sind default-Konstruierbar
> ist? also der decltype-Trick nicht funktioniert?

Probier einfach mal info_t als Parameter dem Template zu übergeben. Ohne 
genaue Compilermeldung kann ich dir sonst nicht viel helfen.
1
// nicht getestet, aber dann nicht über decltype sonder struct mit type.
2
template<size_t ... I>
3
struct collect_infos
4
{
5
  using infos = std::tuple<info_t<I>...>;
6
}

von CppBert (Gast)


Lesenswert?

hier zum oline kompilieren: https://gcc.godbolt.org/z/Hezp_U

in Zeile 24 kannst du die "Variante" einstellen

0 mein aktueller Stand
1 dein Tip - der mir sehr gefaellt aber irgendwie nicht das richtige 
macht
  collect_infos<2> => std::tuple<info_t<2>>
  collect_infos<5> => std::tuple<info_t<5>>

2 dein Tip mit info_t als Item Template-Parameter - würde vielleicht 
gehen wenn das restliche Verhalten richtig waere

von CppBert (Gast)


Lesenswert?

info_t als Template-Parameter funktioniert so

template<template<size_t> typename Item

nicht einfach

template<typename Item

geschrieben werden

sonst funktioniert das mit der spezialisierung nicht

von CppBert (Gast)


Lesenswert?

und so sieht der "fix" für dein collect_infos aus

an einem Beispiel orientiert:
https://stackoverflow.com/questions/39995149/expand-a-type-n-times-in-template-parameter
1
  template<typename>
2
  struct AHelper;
3
4
  template<std::size_t... S>
5
  struct AHelper<std::index_sequence<S...>> {
6
    using type = std::tuple<info_t<S>...>;
7
  };
8
9
  template< size_t N >
10
  struct A : AHelper<std::make_index_sequence<N>> {};
11
12
  static_assert(std::is_same<A<2>::type, std::tuple<info_t<0>, info_t<1> >>::value, "not the same");

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.