2009-07-09 12 views
1

Ich habe für einige meiner Klassen verschiedene Implementierungen pro OS.
Meine Source-Struktur ist wie folgt:Multiplatform C++ Projekt: Aufnahme von plattformspezifischen Quellen

  • umfassen/AExample.h
  • umfassen/windows/WindowsExample.h
  • include/linux/LinuxExample.h
  • src/AExample.cpp
  • src/windows/WindowsExample.cpp
  • src/linux/LinuxExample.cpp

die A * -Klassen sind die Schnittstellen für die spezifischen Implementierungen

Mein aktuelles Buildsystem ist CMake - aber im Moment ist es nur in der Lage, die Linux-Version zu bauen.

In einem Fenster bauen muss ich nur die Fenster schließen/* Datei und auf Linux nur die Linux/* Dateien

I

  • sind nur die Dateien zu

    müssen, die für ein aktuelles relevant sind bauen
  • die richtige Implementierung wählen, wenn ich eine Instanz von AExample brauchen

Welche Techniken können Sie diese in einer professionellen Art und Weise zu realisieren, empfehlen?

Antwort

7

Dies ist ziemlich einfach in CMake.

Lassen Sie einfach Ihre CMakeLists.txt-Datei die Plattform überprüfen, fügen Sie die entsprechenden Dateien hinzu oder fügen Sie das entsprechende Unterverzeichnis nach Bedarf hinzu.

Die grundlegende Syntax ist:

IF(WIN32) 
    # Do windows specific includes 
ELSEIF(UNIX) 
    # Do linux specific includes 
ENDIF(WIN32) 
2

Wenn die Header die gleichen Namen haben, aber sie befinden sich in unterschiedlichen Hierarchien können Sie festlegen, nur die -I (einschließlich Pfad) Flag richtig für Ihren Compiler. Das ist viel einfacher, als alle plattformabhängigen Includes separat zu behandeln.

Wenn die Header im selben Verzeichnis befinden und/oder Sie wollen Sachen gestalten, würden Sie normalerweise dies in Ihrem C/C++ Code:

#ifdef _WIN32 
    .. include Win headers .. 
#endif 

#ifdef LINUX 
    .. include Linux headers ... 
#endif 

Ich würde die cmake spezifische Lösung nicht empfehlen, es sei denn, Sie sind sicher, dass Sie das Build-System später nicht mehr wechseln müssen.

+1

Dies behandelt jedoch nicht die Aufnahme verschiedener Quelldateien im Build. Die Verwendung von CMake führt normalerweise zu einer entsprechenden Einstellung, aber Sie können auch plattformspezifische Quelldateien hinzufügen. Das ist einer der (vielen guten) Gründe, CMake überhaupt einzusetzen. –

+0

Dies ist eine dieser Techniken, die einfach ist und für kleine Projekte gut funktioniert, die aber problematisch wird, wenn Projekte größer werden. _Großes Scale C++ Software Design_ empfiehlt gegen das Bestreuen von Plattformüberprüfungen in Ihrer gesamten Quelle. Ich denke, das Build-System zu verwenden, um plattformspezifischen Code zu verwalten, ist am besten, aber wenn Sie es in der Quelle selbst tun möchten, ist es ratsam, eine [stark strukturierte Organisation] zu verwenden (http://stackoverflow.com/a/31304341/365496). – bames53

0

Obwohl ich glaube, dass die Verwendung des Build-Systems für plattformübergreifende Dateien am besten ist, ist es möglich, einfach Ihre Quelle zu schreiben, um sie über den Präprozessor zu verarbeiten. Jedoch ist die sehr übliche, schnelle und schmutzige Methode, dies zu tun, wo Sie Plattform-Checks über den gesamten Code streuen, problematisch und sollte vermieden werden.

Stattdessen sollten Sie eine stärker strukturierte Methode zum Organisieren von plattformspezifischem Code verwenden, um Probleme zu vermeiden, wenn das Projekt größer wird.

Plattformspezifischer Code sollte auf Dateiebene eindeutig identifiziert werden: Verwenden Sie zum Beispiel eine Verzeichnisstruktur mit Verzeichnissen für jede Plattform und legen Sie alle plattformspezifischen Quelldateien in das entsprechende Verzeichnis. Quelldateien sollten keinen Code für mehrere Plattformen oder Präprozessor-Plattformprüfungen enthalten (außer für eine Ausnahme). Darüber hinaus sollte plattformspezifische Quelle in der Regel nicht direkt gebaut werden oder #include d. Stattdessen sollte plattformspezifischer Code nur indirekt über nicht plattformspezifische Quelldateien verwendet werden, die nichts außer den Plattform-Checks und #include s der plattformspezifischen Dateien enthalten.

Beispiel-Source-Organisation:

src/ 
    main.cpp 
    foo.h 
    foo.cpp 
    windows/ 
    foo.cpp.inc 
    posix/ 
    foo.cpp.inc 

Beispiel-Source-Inhalt:

// main.cpp 
#include "foo.h" 

int main() { 
    foo(); 
} 

// foo.h 
void foo(); 

// foo.cpp 
#if defined(_WIN32) 
# include "windows/foo.cpp.inc" 
#elif __has_include(<unistd.h>) 
# include<unistd.h> 
# if defined(_POSIX_VERSION) 
# include "posix/foo.cpp.inc" 
# endif 
#else 
# error Unknown platform 
#endif 

// windows/foo.cpp.inc 
#include "foo.h" 

#include <iostream> 
#include <Windows.h> 

void foo() { 
    std::cout << "Windows\n"; 
} 

// posix/foo.cpp.inc 
#include "foo.h" 

#include <iostream> 
#include <unistd.h> 

void foo() { 
    std::cout << "POSIX\n"; 
} 

Windows-Build und Ausgang:

cl.exe /EHsc /W4 /WX src\main.cpp src\foo.cpp
main

Windows-

Linux bauen und Ausgang:

g++ -Wall -Wextra -Werror -Isrc src/main.cpp src/foo.cpp
./a.out

POSIX

Die im obigen Beispiel gezeigte Methode funktioniert recht gut, wenn Code für verschiedene Plattformen vernünftig die gleiche Dateiorganisation verwenden kann. Wenn der Code für verschiedene Plattformen so unterschiedlich ist, dass Sie für jede Plattform unterschiedliche Dateistrukturen benötigen, ist es schwieriger, diese Technik zu verwenden, und die Umstellung auf das Build-System zur Verwaltung des Codes für verschiedene Plattformen wird klarer.

Es ist auch möglich, diese Technik mit dem Build-System zu mischen; Code, der die Dateistruktur plattformübergreifend nutzt, kann dies verwenden, während Module, die für verschiedene Plattformen spezifisch sind, vom Build-System verarbeitet werden können.