[C++] Datentyp abfragen

[Moepi]1

Lt. Commander
Dabei seit
Jan. 2002
Beiträge
1.233
Ich hab mir eine Template Klasse wie folgt gebaut:

Code:
template <class T> void MKLBench
{
  public:
    MklBench(int m, int n, int k);
    void execute();
    ...
};
Problem ist jetzt, dass die Methode execute() anhand des Datentyps T entscheiden muss, ob sie die Methode SGEMM() oder DGEMM() der MKL (Math Kernel Library / Matrixmultiplikation) aufruft.
Ich bräuchte also innerhalb von execute() so ein Konstrukt:

Code:
...
if( T == float )
{
  SGEMM();
}
else
{
  DGEMM();
}
...
Die Frage ist jetzt, ob sowas überhaupt geht, wenn ja wie bzw. wenn nein, wie umgehe ich das Problem?

Danke schonmal im Voraus!


/Edit: Ich denk es wird nicht gehen - die Template Direktiven werden ja vom Compiler statisch ausgewertet und nicht zur Laufzeit. Ich werd mir Codegeneratoren schreiben, die mir die verschiedenen Quelltexte erzeugen.
Falls jemandem noch was einfällt, darf er es aber gerne posten (:
 
Zuletzt bearbeitet:
P

Philifish

Gast
Ob das jetzt geht, weiß ich nicht genau. Aber zumindest teilweise könnte man es mit sizeof umgehen.
Wenn sizeof(T)==1 dann ist T nen char
Wenn sizeof(T)==4 dann ist T nen double
...
Aber des stimmt natürlich ncith immer,
Wenn sizeof(T)==4 dann könnte T auch nen char[4] sein...
 

Boron

Commander
Dabei seit
Sep. 2001
Beiträge
2.784

[Moepi]1

Lt. Commander
Ersteller dieses Themas
Dabei seit
Jan. 2002
Beiträge
1.233
Ob das jetzt geht, weiß ich nicht genau. Aber zumindest teilweise könnte man es mit sizeof umgehen.
Wenn sizeof(T)==1 dann ist T nen char
Wenn sizeof(T)==4 dann ist T nen double
...
Aber des stimmt natürlich ncith immer,
Wenn sizeof(T)==4 dann könnte T auch nen char[4] sein...
Würde fast klappen, aber leider dürften Single Precision Complex Floats und Double Precision Real Floats dieselbe Größe haben... (:

Aber die anderen beiden Tips schau ich mir bei Gelegenheit an. Mal schauen, ob ich das noch in das schnöde kleine Benchmark implementiere :)

Danke!
 

7H3 N4C3R

Lt. Commander
Dabei seit
Feb. 2002
Beiträge
1.816
Templatespezialisierung:
Code:
template <typename T>
void f();

template <>
void f<float>()
{
   SGEMM();
}

template <>
void f<double>()
{
   DGEMM();
}
Was Du willst geht alles mit Templates. Allerdings kann's an ein paar Punkten hakelig werden, speziell bei den Methoden.
 
Zuletzt bearbeitet:

7H3 N4C3R

Lt. Commander
Dabei seit
Feb. 2002
Beiträge
1.816
Sooo, dann antworte ich jetzt mal etwas detaillierter, da ausreichend Zeit und so. Ich mach das mal der Übersichtlichkeit halber als Doppelpost. Falls sich jemand dran stört - bitte zusammenführen.

Alsooo:
Alles was du willst geht mit Templates. Alle Informationen sind zur Compilezeit vorhanden, also kann auch sämtlicher Code zur Compilezeit generiert werden. Laufzeitabfragen á la sizeof(T) == sizeof( float) sind überflüssig (und geben nichtmal den Hauch von Sicherheit), da die entsprechende Methode eh nur für einen einzigen Datentyp durchlaufen wird. Der Code wird schließlich für jeden Datentyp mit dem er instanziert wird generiert. Genauso fällt RTTI raus, da es schlichtweg nicht nötig ist und außerdem für C++-Builtins keine RTTI zur Verfügung steht.

Die Lösung ist allerdings etwas komplizierter und geht etwas auf die Sprachbesonderheiten von C++ ein.

Grundsätzlich musst du die Methoden nur entsprechend spezialisieren:

Code:
template <> void MKLBench<float>::execute() {
  SGEMM();
}

template <> void MKLBench<double>::execute() {
  DGEMM();
}
Das führt aber zu einem fatalen Problem. Der Code hängt an dieser Stelle nicht mehr von einem Templateparameter ab, er ist vollständig spezialisiert. D.h. an dieser Stelle wird Code tatsächlich generiert. Wenn du dann das Headerfile in mehreren Übersetzungseinheiten einbindest, hast du multiple defined symbols und die Anwendung lässt sich nicht mehr linken.

Was also tun?

Die Lösung ist folgende:

Das Headerfile enthält nur die Deklaration des Templates, aber nicht die Definition - sprich Implementierung. Das ist extrem wichtig. Die gesamte Implementierung wandert nun in ein .cpp-File. Evtl. sagst du jetzt "hey das geht nicht, die muss auch ins Headerfile" - abwarten.

Im .cpp-File schreibst du die Templateimplementierung wie gehabt hin. Zusätzlich spezialisierst du die Methode execute wie oben angegeben.

Als letztes kommt in das .cpp-Fille folgendes:
Code:
template class MKLBench<float>;
template class MKLBench<double>;
Das ist eine explizite Instanziierung. Damit wird der gesamte Templatecode generiert und die Spezialisierung bleibt bestehen. Außerdem gibt es so keine unresolveds, da der Code schließlich da ist und instanziiert ist. Deshalb darf im Headerfile aber auch keine Implementierung stehen, da sonst an anderen Stellen noch Code instanziiert werden würde und du damit multiple defined symbols und undefiniertes Verhalten erntest.



Minibeispiel:

temp.h
Code:
#ifndef TEMP_H
#define TEMP_H

template <typename T>
class A
{
public:
	void f();
	void g();
};

#endif
temp.cpp
Code:
#include "temp.h"

#include <iostream>

template <typename T> void A<T>::g()
{
	std::cout << "Ich bin generisch" << std::endl;
}

template <> void A<float>::f()
{
	std::cout << "Ich bin ein float" << std::endl;
}

template <> void A<double>::f()
{
	std::cout << "Ich bin ein double" << std::endl;
}

template class A<float>;
template class A<double>;
main.cpp
Code:
#include <cstdlib>

#include "temp.h"

int main(int argc, char* argv[])
{
	A<double> a1;
	A<float> a2;
	a1.g();
	a2.g();
	a1.f();
	a2.f();
	system( "pause");
}
Ausgabe:
Ich bin generisch
Ich bin generisch
Ich bin ein double
Ich bin ein float

Kompiliert und getestet mit MSVC++2005. Code ist standardkonform und von jedem anständigen Compiler nutzbar. Falls du noch eine Uraltkrücke á la MSVC++ 6 benutzt - sofort upgraden :D

Entschuldige bitte die Lösung mit system("pause"), falls du unter Linux bist, ich bin gerade faul :) Und noch wichtig - für jeden Datentyp für den Du das Template nutzen willst, musst du dann explizit instanziieren.

Alles klar? :) Erfolgsrückmeldung wäre nett.
 
Zuletzt bearbeitet:
Top