2

So I have a class definition, and I want to add a member variable that can be 1 of 2 different classes depending on the operating system this code is run on.

Is there anyway to do this in C++ so that I can initialize a different class for the "operating_system" member variable depending on some arguement or variable when initializing MyOperatingSystem?

#include <iostream>
#include "Win.h"
#include "Lin.h"

using namespace std;

typedef int os_type;
enum {Win, Lin};

class MyOperatingSystem {
  public:
    MyOperatingSystem(int ver, string n, os_type os);
  private:           
    int version;
    string name;
    // operating_system // want this to be either (Windows win | Linux lin)

};

// constructor
MyOperatingSystem::MyOperatingSystem(int ver, string n, os_type os){
    version = ver;
    name = n;
    if (os == Win){
        // operating system = Windows(int i);
    }
    else{
        // operating system = Linux(int i)
    }
}

Win.h and Lin.h are as follows

Win.h:

#include <windows.h>
class Windows{
    public:
        Windows(int i){
            integer = i;
            mystring = "WinString";
        }
    private:
        int integer;
        LPCWSTR mystring;
};

Lin.h:

#include <termios.h>
class Linux{
    public:
        Linux(int i){
            integer = i;
            mystring = "LinString";
        }
    private:
        int integer;
        cc_t* mystring;
};
  • Very related: https://stackoverflow.com/questions/843389/the-pimpl-idiom-in-practice – πάντα ῥεῖ Jun 14 '22 at 16:24
  • 1
    For your specific use case, you might profit from the already existing implementations like e.g. ncurses. – πάντα ῥεῖ Jun 14 '22 at 16:26
  • Do you want to use the Windows implementation only on Windows platforms and the Linux implementation only on Linux, or both implementations on both platforms? – Spencer Jun 14 '22 at 16:37
  • 3
    Create an abstract baseclass with pure virtual functions (interface, non-copyable, non-moveable), provide 2 different implementations and depending on the platform instantiate one or the other, use the interface in all other places of the code (compile time ifdef). As a bonus, the interface will also allow you to create mocks and stubs for unit testing. – Pepijn Kramer Jun 14 '22 at 16:41

4 Answers4

6

I suggest making a compile-time decision.

Example:

#pragma once // MyOperatingSystem.h

class IOperatingSystem {
public:
    virtual ~IOperatingSystem() = default;

    // misc operations:
    virtual foo() = 0;
};

#ifdef _WIN32
#include "internal/Win.h" // in here MyOperatingSystem  implements  IOperatingSystem 
#else
#include "internal/Lin.h" // in here MyOperatingSystem  implements  IOperatingSystem 
#endif

You don't necessarily need virtual here but it helps when designing to make sure that both implementations follow the same interface.

Ted Lyngmo
  • 93,841
  • 5
  • 60
  • 108
  • This doesn't necessarily need to be virtual. – Spencer Jun 14 '22 at 16:42
  • Why make this virtual? – CoffeeTableEspresso Jun 14 '22 at 17:44
  • 1
    @CoffeeTableEspresso I'd design this class mainly by adding pure virtual methods to the interface. A compilation with either Windows or Linux as a target would show if I've overridden all these methods and that the signatures matches. It's convenient. One can also easily make "flavors". It could be `internal/unixlike.h` and in there you could have layers of inheritance, all chosen at compile-time. One of the "unix-like" OSes may need to `override` a default implementation of a method. No problem. – Ted Lyngmo Jun 14 '22 at 17:49
1

Most obvious solution is to have a base OperatingSystem class with a common interface, and derive your Win and Linux from it.

Vlad Feinstein
  • 10,960
  • 1
  • 12
  • 27
0

The operating system is a compile time thing so you can use a template parameter

template <ostype os> class MyOperatingSystem ...

You can then use if constexpr to check which os you have and include different code in the class. The Windows and Linux classes should probably have a common base class and then most of the differences would be solved by virtual methods. So if constexpr (os == Win) ... should be the exception.

Another way to go would be a dependency insertion:

MyOperatingSystem(int ver, string n, Os & os);

where Os is the common base class for Windows and Linux.

Or again, since this is a compile time thing:

template <typename Os> class MyOperatingSystem : public Os { ... }
template <typename Os> class MyOperatingSystem  { ... Os os; }

In this case you don't even need a common base class (but it helps to ensure a common interface). Both Windows and Linux just have to follow a common interface, like defining an os_type. If Os::os_type is a constexpr you could use if constexpr again. But I think the point of the Os classes is to encapsulate all the OS specific code so again that should be rare if used at all.

Note that with the Os template way the MyOperatingSystem class is (or has) a concrete Windows or Linux class. So all the virtual function calls get replaced by concrete calls. So no overhead. The earlier cases have a pointer or reference to the base class and will need to do virtual method calls.

Goswin von Brederlow
  • 11,875
  • 2
  • 24
  • 42
-3
std::variant<Windows, Linux> operating_system;

std::variant is a sum type -- it is one of the types in the list of types.

As a side effect, it carries with it the type it is -- so the os_type variable is redundant to store beside it.

Yakk - Adam Nevraumont
  • 262,606
  • 27
  • 330
  • 524