11
votes

Plate-forme multi-plateforme en C ++

Bien sûr, je sais que la meilleure réponse est "N'écrivez pas votre propre code multi-plate-forme, quelqu'un a déjà fait ce dont vous avez besoin", mais je fais cela comme un atout droit / apprentissage et non dans aucun exercice payé. capacité. Fondamentalement, j'écris une application de console petite en C ++, et j'aimerais faire une plate-forme croix, traitant de choses comme des fichiers, des prises et des threads. Oop semble être un excellent moyen de gérer cela, mais je n'ai pas vraiment trouvé de bon modèle pour écriture de cours qui partagent la même plate-forme croisée d'interface.

L'approche facile consiste à simplement planifier une interface méta-interface, utilisez-la tout au long de la Le reste du programme et compilez simplement la même classe avec différents fichiers en fonction de la plate-forme, mais je me sens comme il y a une meilleure façon qui est plus élégante; à tout le moins, quelque chose qui ne confondrait pas Intellisense et son ILK serait bien. P>

J'ai examiné certaines des plus petites classes de la source WxWidgets et utilisent une approche qui utilise Un membre privé détenant des données pour la classe, par exemple P>

Foo* CreateFoo();


0 commentaires

8 Réponses :


15
votes

Pour mettre en œuvre des fonctionnalités de la plate-forme croisée nécessitant une prise en charge du système d'exploitation, (comme des sockets), vous devrez écrire du code que simplement ne compilera pas sur certaines plates-formes. Cela signifie que votre conception orientée objet devra être complétée par des directives de préprocesseur.

Mais puisque vous devrez utiliser le préprocesseur quand même, il est douteux de savoir si quelque chose comme un win32socket (par exemple) qui hérite d'un la classe de base est même nécessaire ou utile. OO est généralement utile lorsque différentes fonctions seront choisies polymorphiquement au moment de l'exécution. Mais la fonctionnalité multiplate-forme est généralement plus d'un problème de compilation. (Par exemple, il n'y a pas d'utilisation en appelant polymorphique win32socket :: Connect si cette fonction ne compile même pas sur Linux!) Par conséquent, il pourrait être préférable de créer simplement une classe classe Ce qui est implémenté différemment en fonction de la plate-forme à l'aide de directives de préprocesseur.


4 commentaires

Il n'est pas vraiment nécessaire de jeter votre code avec des conditionnels de préprocesseur - il suffit de mettre du code spécifique à la plate-forme dans des fichiers séparés et de la sélection dans des scripts de fabrication / de construction.


Droit; Les directives de préprocesseur peuvent être aussi simples que d'inclure différents fichiers d'en-tête en fonction de la plate-forme.


Absolument - ma question était plus sur la manière dont on devrait faire la création des différentes implémentations pour une classe de socket générique. Toutes mes excuses pour l'ambiguïté là-bas.


@Shz, je comprends que, mais ce que je dis, c'est que des fonctions virtuelles, et OO en général, constitue un mécanisme d'exécution et donc pas vraiment utile ni nécessaire aux fins de la conception du code multiplate-forme.



1
votes

La deuxième approche est meilleure car elle vous permet de créer des fonctions membres qui n'existeront que sur l'une des plates-formes. Ensuite, vous pouvez utiliser la classe abstraite dans la plate-forme-globe -Code et la classe de béton du code spécifique à la plate-forme.

Exemple: P>

class Foo
{
    public:
        virtual ~Foo() {};
        virtual void Bar() = 0;
};   
class Win32Foo : public Foo
{
    public:
        void Bar();
        void CloseWindow(); // Win32-specific function
};

class Abc
{
    public:
        virtual ~Abc() {};
        virtual void Xyz() = 0;
};   
class Win32Abc : public Abc
{
    public:
        void Xyz()
        {
           // do something
           // and Close the window
           Win32Foo foo;
           foo.CloseWindow();
        }
};


0 commentaires

2
votes

Idéalement, vous concentreriez les différences au niveau le plus bas possible.
Donc, votre code de niveau supérieur appelle FOO () et seulement FOO () a besoin de prendre soin de l'interne de ce que l'OS appelle.

ps. Jetez un coup d'œil à «Boost», il contient beaucoup de choses pour gérer les systèmes de fichiers de réseau de plateformes croisées etc.


0 commentaires

14
votes

Définir votre interface, qui transmet à Détail CODE> Appels:

namespace detail
{
    void some_thing(void);
}


3 commentaires

C'est une bonne idée et je vais certainement jeter un coup d'œil à ce que Boost y fait. Je suis curieux cependant, ce modèle permet-il que l'état de classe varie entre les implémentations? Par exemple, les poignées de fichier POSIX VS Win32 ont un type différent - si j'écris un wrapper de fichier, les structs / classes d'état devraient être différents.


Vous effectueriez une classe File_handle , et il suffit de supporter la poignée dans un vide * , habituellement. La mise en œuvre peut la jeter à ce qu'elle a besoin. Ceci est garanti sûr par la norme. Cette méthode fonctionne également bien si vous chargez de manière dynamique des implémentations, car vous venez de remplacer détail :: Quel que soit le document avec quelque_fonction_poing , chargé de la bibliothèque.


+1: Déléguer toujours au système de construction Qu'est-ce que le temps de construction spécifique



1
votes

Déléguer comme dans votre premier exemple ou des œuvres de Gman de Gman, surtout si la partie spécifique à la plate-forme nécessitera de nombreux membres spécifiques à la plate-forme (par exemple, des membres de types qui n'existent que sur une plate-forme). Inversez-vous, vous devez conserver deux classes.

Si la classe va être plus simple, sans beaucoup de membres spécifiques à la plate-forme, vous pouvez simplement utiliser une déclaration de classe commune et écrire deux implémentations dans deux fichiers .CC différents (plus peut-être un troisième .cc pour la méthode indépendante de la plate-forme. implémentations). Vous aurez peut-être besoin de quelques #Ifdefs dans le fichier d'en-tête pour quelques membres ou membres spécifiques à la plate-forme avec des types spécifiques à la plate-forme. De cette façon, c'est plus un hack, mais peut être plus facile à commencer. Vous pouvez toujours passer aux délégués s'il devient hors de contrôle.


0 commentaires

4
votes

Vous n'avez pas besoin de passer au-dessus de la plate-forme. La plate-forme transversale est plus d'une architecture et d'un paradigme design.

Je suggère d'isoler tout le code spécifique de la plate-forme du code standard, tel que Sockets Gui. Comparez-les sur les différentes plates-formes et écrivez une couche ou une enveloppe générique. Utilisez ce calque dans votre code. Créez des fonctions de bibliothèque pour implémenter la mise en œuvre spécifique de la plate-forme de la couche générique.

Les fonctions spécifiques à la plate-forme doivent être dans des bibliothèques ou des fichiers distincts. Votre processus de construction doit utiliser les belles bibliothèques pour les plates-formes spécifiques. Il ne devrait y avoir aucune modification de votre code "standard".


0 commentaires

1
votes

Comme d'autres ont noté, l'héritage est un mauvais outil pour le travail. La décision du type concret à utiliser dans votre cas est faite au moment de la construction et le polymorphisme classique permet de prendre la décision à l'exécution (c'est-à-dire à la suite de l'action des utilisateurs).


0 commentaires

1
votes

Vous pouvez utiliser la spécialisation de modèle pour séparer le code pour différentes plates-formes.

enum platform {mac, lin, w32};

template<platform p = mac>
struct MyClass
{
 // Platform dependent stuff for whatever platform you want to build for default here...
};

template<>
struct MyClass<lin>
{
 // Platform dependent stuff for Linux...
};

template<>
struct MyClass<w32>
{
 // Platform dependent stuff for Win32...
};

int main (void)
{
 MyClass<> formac;
 MyClass<lin> forlin
 MyClass<w32> forw32;
 return 0;
}


1 commentaires

Entré sur ce "idiome" ici aussi: alttevblog.com / 2011/06/7/ ...