0

I am a newbiee in C++. I have two pure abstract classes (like interfaces), and I derive a Class from these two pure abstracts classes.

In a case, I need to upcast the derived class pointer to one of the base abstract classes. First of all, is there any limitation on that.

class IBase1
{
  virtual ~IBase() = default;
}

class IBase2
{
  virtual ~IBase2() = default;
}

class Derived : public IBase, public IBase2
{
}

Derived d;
IBase1* basePtr = dynamic_cast<IBase1*>(&d);

Herein, if I use any other cast, I am not pretty sure but I get invalid pointer from the cast, so I need to use dynamic_cast for upcasting from multiple inheritance, is that right?

When I do that, I get an error "source type is not polymorphic"

My base classes are pure abstract classes so they have at least one virtual methid so it should be ok, right but why I get this error? Is it about the multiple inheritance?

Edit: There are one more layer here. My Derived Class needs to contain two different type of instances but my instances are really huge variable so as a C developer :), I was planning to use an Union for less memory usage. The union has only instances of two classes which derived from pure abstract classes. So, I was assuming that the union instance address should also points the offsets of my class instances, but C++ cannot probably know the write member methods address.

class IFile
{
public:
  virtual ~IFile() = default;
};

class IDirectory
{
public:
  virtual ~IDirectory() = default;
};

class FileSystem1 : public IFile, public IDirectory
{
public:
  FileSystem1() { }

  virtual ~FileSystem1() final override = default;
private:
   Native1APIInstance file;
};

class FileSystem2 : public IFile, public IDirectory
{
public:
  FileSystem2() { }

  virtual ~FileSystem2() final override = default;
private:
   Native2APIInstance file;
};

union FileSystemInstance
{
  FileSystem1 fs1;
  FileSystem2 fs2;
  FileSystemInstance(string path)
  {
    if (path[0] == '1') // initialise fs1
    else if (path[0] == '2') // initialise fs2
  }
};

FileSystem fs("<PATH to File System>");
IFile* file = reinterpret_cast<IFile*>(&fs);

Herein, I dont want to interest which instance is initialised. I want to work with only the base class interface. I am wondering is that somehow possible with an Union?

Thanks.

muratcakmak
  • 325
  • 2
  • 14
  • 4
    Fix the code so that it shows the problem, rather than a bunch of typos. – Pete Becker Oct 14 '19 at 21:15
  • 3
    You don't need *any* cast. `Derived` already is of type `IBase`, and `Derived*` is convertible to `IBase*` (the reverse is not true). Also, after fixing a lot of typos, [I cannot reproduce the problem](https://wandbox.org/permlink/XP69YhfxEU4AVySR) – Yksisarvinen Oct 14 '19 at 21:15
  • 6
    `dynamic_cast` is not needed in this case. `IBase* basePtr = &d` will work just fine. You need the dynamic cast when going from base to derived. – cplusplusrat Oct 14 '19 at 21:16
  • Please post a [mcve]. Also already has been pointed out, the code you posted has many typos, but not the error you claim – 463035818_is_not_an_ai Oct 14 '19 at 21:26
  • 1
    If you really need to upcast your object into its base-class you should also rethink your design. – Jens Oct 14 '19 at 21:32
  • 2
    @Jens I think you mean the opposite. Upcasting a polymorphic type is very common; it's hard-coded _downcasting_ that usually indicates a design error. – cdhowie Oct 14 '19 at 21:47
  • 1
    "I need to upcast the derived class pointer to one of the base abstract classes" You _never_ need to do what you describe, because an instance of "Derived" class is, by definition, also a base class. So, you simply use your 'd' as if it were an 'IBase'. For example, if "IBase" had a function "foo", you simply invoke d.foo(). NO CAST REQUIRED. Just try it! – 2785528 Oct 14 '19 at 22:01
  • I am sorry all, I have given missing information. My problem has one more layer, I have edited my description which may explain why I get the error. It is may bad. – muratcakmak Oct 14 '19 at 22:12
  • 1
    A filesystem is not a file nor a directory. But anyway you could just write `IFile* file = &fs.fs1` – rustyx Oct 14 '19 at 22:41
  • If you are a newbie, then you should probably avoid union. They are rarely used even by expert and usually only for basic types like `int` or `char` and for low level stuff. – Phil1970 Oct 14 '19 at 22:57
  • 1
    Good design generally don't need much casting. Often explicit cast is a work around poor design or system restriction. – Phil1970 Oct 14 '19 at 22:58

2 Answers2

2
class FileSystem1 : public IFile, public IDirectory

Let's sit back and think about just that much for a moment. That asserts that a FileSystem1 is (or more formally, under any possible circumstances, can be used in place of) either an IFile or an IDirectory.

At least as most people use these terms, that's not how things are at all. As most people use the terms, a File system contains some things, each of which can be either a file or a directory:

class FS_node {
    virtual std::string name() const { return name_; }
    // ...
    virtual ~FS_node = default;
};

class File : public FS_node {
    // ...
};

class Directory : public FS_node { 
    // ...
};

class FileSystem { 
    std::vector<FS_node *> nodes;
public:
    // ...
};

Now, from the sound of things, you have to deal with two entirely separate file systems. There are a number of ways of doing that. One possibility would be to have a base FileSystem class that defines an interface to a file system, then have two derivative classes that implement that interface in terms of two separate APIs at the OS level.

Another possibility would be to implement similar functionality, but instead of using inheritance, you'd specify the API interface class as a template parameter when you instantiate a FileSystem object:

template <class Api>
class FileSystem {
    Api api;
public:
    FileSystem(Api const &api) : api(api) {}

    // FS functions for finding files and such go here,
    // each implemented via the `Api` passed as a parameter
};

As to the difference between using templates and inheritance: it's pretty much the same as usual: templates are static, so if (for example) you want code that you can specify at compile time whether to compile for Windows or Linux, templates should work nicely. On the other hand, if you're dealing with something like a single collection of file systems, and that collection might contain a mixture of objects, each representing a different file system, and you want to be able to deal with all of them transparently at run time, then you'll probably need to use an inheritance hierarchy.

But at least to me, it seems fairly likely that your original design with FileSystem derived from both File and Directory classes is almost certainly a pretty serious mistake. We'd probably need to know a bit more about what you're doing to be sure of what approach is really optimal, but that's probably not it.

Ignoring all of that about the design for a moment, and looking at the question of how to convert from pointer to derived to point to base, we really have only two cases that matter a whole lot. If you used public inheritance (as shown in the question), the conversion doesn't require a cast at all:

FileSystem1 foo1;

IFile *fileBase = &foo1;      // No problem.
IDirectory *dirBase = &foo1;  // Likewise

If you used private derivation, then you've just found an exceptionally rare situation: one where you actually need to use a C-style cast to do the conversion properly:

class Base1 {};
class Base2 {};

// Note: private derivation: 
class Derived : Base1, Base2 {};

Derived d;
Base1 *b1 = (Base1 *)&d;
Base2 *b2 = (Base2 *)&d;

For this specific case (converting a derived class to an inaccessible base class) none of the "new" C++ casts can do the job--you must us a C-style cast.

Jerry Coffin
  • 476,176
  • 80
  • 629
  • 1,111
  • Hi @jerry-coffin, thank you for the detail information. It is an embedded software. We have different types of physical drives which have different file systems (let's say Fat, NTFS). The limitation in our system is dynamic memory is not allowed (we cannot use even STL). So, the relation must be companisation; once we create a FileSystem, Specific File System must be created too. So, the first recommendation does not work for me, I cannot work with pointers here which needs separate instantination outside of the FileSystem. – muratcakmak Oct 15 '19 at 08:04
  • For the second recommendation (template), I was trying to abstract all these details with the file path. My idea was if the path shows a physical drive X(NTFS), just initialise for the NTFS implicitly, so the user application will not have to know the file system while accessng a file. So, I use a Template, I will need to pass the low level File System Instance like below; which I was trying to avoid FileSystemInstance f("0:/A.txt"); FileSystemInstance f("1:/A.txt"); – muratcakmak Oct 15 '19 at 08:04
0

It seems the problem is about Union data structure. For example, if I use union while the following works;

FileSystem fs; 
fs.fs1.Func();

the below does not work

FileSystem fs; 
FileSystem1* fs1 = &fs.fs1; (No casting, the same type)
fs1->Func();

I got an exception for the second case, it somehow cannot find the virtual function table (Access violation for vtable; 0xCCCCCCCC). Does not make sense to me.

If I change Union to Class type, the same code works. It must be about the Union itself. I need to study it well, or leave the idea. Thank you all.

muratcakmak
  • 325
  • 2
  • 14