[C++] Function Tracker

Programmiersprachen, APIs, Bibliotheken, Open Source Engines, Debugging, Quellcode Fehler und alles was mit praktischer Programmierung zu tun hat.
Antworten
Dragontear
Beiträge: 5
Registriert: 07.06.2007, 13:38

[C++] Function Tracker

Beitrag von Dragontear »

Hallo,
ich schreibe mir gerade einen Function Tacker, der mir alle augerufenen Funktionen in einem vektor speichert. Ich habe eine Klasse A, die den statischen vektor und die statischen Funktionen, um in den vektor zu schreiben und aus ihm elemente zu löschen, besitzt.
Daneben existiert eine Klasse B, die beim instanzieren im Konstruktor die statische Funktion zum speichern des Funktionsnamen von Klasse A aufruft und im Destruktor dann die statische Funktion von Klasse A aufruft, um den Funktionsnamen wieder zu löschen.

Code: Alles auswählen

class A{
		friend class B;
	private:
		static std::vector<const char*>	_stringVectorGlobal;

		static void					_pushFunction(const char* l_FuncName);
		static void					_popFunction();
	public:
		A() {}

};

class B{
	public:
		B(const char* l_FuncName){
			A::_pushFunction(l_FuncName);
		}
		~B(){
			A::_popFunction();
		}
			
};
Wenn ich nun in einer Funktion eine Instanz von B erstelle, dann speichert er mir den Funktionsnamen im statischen Vektor. Beim Verlassen der Funktion wird die Instanz von B zerstört und der Funktionsname wird aus dem statischen Vektor gelöscht. Somit kann ich bei einer Exception mir die aufgerufenen Funktionen ausgeben lassen.

Diesen FunktionsTracker möchte in einer eigenen lib speichern, die ich dann in anderen eigenen libs verwende.
Zusätzlich will ich das Tracken in eigenen Projekten an und ausschalten. Wenn das Programm also erstmal fehlerfrei läuft, dann schalt ich den Tracker aus und spare somit ein wenig Rechenzeit und Speicher.
Meine Frage ist nun, wie ich das umsetzen kann?

Einerseits könnte man es mit Präprozessdirektiven in der FunctionTracker-Lib lösen

Code: Alles auswählen

#if DEFINE_FUNCTIONTRACKER
#define track()       static const char *INTERNAL_FUNCNAME = __FUNCTION__;    \
                              B INTERNAL_TRACK(INTERNAL_FUNCNAME);
#else 
#define track()
#endif
Das Problem hierbei ist aber, dass alle anderen Libs, die diese FunctionTracker-Lib einbinden das Makro track() wegoptimieren, wenn in der jeweiligen lib DEFINE_FUNCTIONTRACKER = 0 ist und bei DEFINE_FUNCTIONTRACKER = 1 es sich nicht im Projekt ausschalten lässt. D.h. wenn ich in meinem Projekt

Code: Alles auswählen

#define DEFINE_FUNCTIONTRACKER 1 
definiere, aber die lib mit DEFINE_FUNCTIONTRACKER = 0 kompiliert wurde, dann wird nicht getrackt, obwohl es gewollt ist und umgekehrt.

Ein andere Lösung wäre mit Templates:

Code: Alles auswählen


template <bool>
class B
{
    public:
       B( const char* l_FuncName ) : _param( l_FuncName ) { A::_pushFunction( l_FuncName ); }
      ~B() { A::_popFunction( l_FuncName); }
    private:
       const char* _FuncName;
};

template <>
class B <false>
{
   public:
      B( const char* l_FuncName) {}
      ~B() {}
};

In der Klasse A würde eine statische Variable deklariert werden:

Code: Alles auswählen

static bool A::log = true;
In allen Funktionen würde ich nun immer B<A::log>("...") instanzieren, um zu tracken. Bei A::log == true würde das Tracking stattfinden. Bei A::log == false würde kein Tracking stattfinden. Nun stellt sich mir aber die Frage, ob der Compiler B<false>("...") wegoptimiert?
Somit könnte ich zu Beginn eines jeden Projekts A::log setzen, um zu tracken (true) oder es sein zu lassen (false). Beim letzteren Fall (false) weiss ich aber nun nicht, ob die Instanzierung wegoptimiert wird, um somit Ressourcen und Rechnzeit zu sparen.
Weiss jemand, ob dies der Fall ist oder wie ich das sicher ermitteln kann oder hat vlt. jemand einen ganz anderen Ansatz?
Benutzeravatar
Krishty
Establishment
Beiträge: 8268
Registriert: 26.02.2009, 11:18
Benutzertext: state is the enemy
Kontaktdaten:

Re: Function Tracker

Beitrag von Krishty »

Defines sind ungeeignet – weil sie die Einstellung nicht über Lib-Grenzen hinaustransportieren, wie du ja selber schon festgestellt hast. Du solltest die Entscheidung also zum finalen Projekt verlagern, indem class Bs Konstruktor und Destruktor in allen Basisprojekten schlicht undefiniert bleiben. Erst in deiner Exe/DLL/wasauchimmer kompilierst du dann – je nachdem, ob du tracken willst oder nicht – B::B(const char *) und B::~B() entweder mit einem Aufruf an A::_pushFunction() bzw A::_popFunction() oder als leere Funktionen. Bein Linken wird entsprechend in allen B benutzenden Funktionen (auch denen aus den vorkompilierten Bibliotheken) das von dir gesetzte Funktionspaar gelinkt. Ist es leer, optimiert der Compiler die Klasse komplett weg – wahrscheinlich; mit LTCG sogar sicher (das gilt auch für die Template-Lösung – ob der Compiler sie überhaupt akzeptiert, weiß ich gerade nicht, weil A::log aus Sicht der Bibliotheken zwar ein statischer integraler Ausdruck, aber eben auch undefiniert ist – es kann aber auch gut sein, dass die temporäre Form des Programm-Codes in die Lib geschrieben wird anstatt dass der Quelltext real kompiliert wird).

Du könntest die beiden Funktionen dann einem Header deines Basisprojekts beifügen, der nur vom endgültigen Projekt aus kompiliert wird (#if !defined(_LIB) o.ä. bietet sich an – aber bedenken, dass nur einmal, also von einer .cpp aus inkludiert werden darf, damit keine mehrfach definierten Symbole auftauchen); so braucht man die beiden Funktionen nicht in jedem Projekt neu schreiben.

Gruß, Ky
seziert Ace Combat, Driver, und S.T.A.L.K.E.R.   —   rendert Sterne
Antworten