Can I define a class member variable in C++ be multiple data types?

Viewed 146

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;
};
4 Answers

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.

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

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.

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.

Related