0

I have two projects one in C and the other in C++ and I try to cast a class object in C++ to a structure object in C. For example, I have an object from myClass which I'm trying to cast to myStru as follows:

In my C++ project I have this class:

class myClass{
    myClass();
    ~myClass();
    char    *data;
    int     var;
}

In my C project I have this structure:

struct myStru {
    char  *data;
    int    var;
};
typedef struct myStru myStru;

Now in my C++ project I create an object from myClass:

myClass *classObj = new myClass();
classObj->data = new char[10];
classObj->var  = 99;

In my C project I receive classObj as a void pointer and I try to cast it as follows:

myStru *struObj = (myStru*)malloc(sizeof(struct myStru));
struObj = (myStru*) classObj;
printf(" struObj->var %d \n", struObj->var); // this print 99

I do this later in my C++ project

   delete []classObj->data; // I know smart pointers can be used here but this is not my point in this question now
    delete  classObj;

Is what I'm doing here right? i.e., casting classObj to struObj in that way?

The full example can be found here (thanks @Borgleader) http://coliru.stacked-crooked.com/a/05543b944ee23f2f

EDIT: I found a good answer to my question in this article (see Accessing C++ Classes from C): https://www.oracle.com/technical-resources/articles/it-infrastructure/mixing-c-and-cplusplus.html

dbush
  • 205,898
  • 23
  • 218
  • 273
Moe
  • 382
  • 4
  • 10
  • its just wording and not essential for your question, but C++ does not have classes and structs. It has class types and you can use either of the two keywords `class` and `struct` to declare them. The only difference is default access and conventions when to use one or the other – 463035818_is_not_an_ai Nov 27 '20 at 13:01
  • 3
    I believe this is undefined behaviour because the types are differnet. – user253751 Nov 27 '20 at 13:02
  • Why do you allocate a pointer to a `myStru`? – Ackdari Nov 27 '20 at 13:02
  • I agree -- I have two projects (one in C and the other in C++) and at some point I need to cast between the two through an interface. I use myClass in C++ and mySrtu in C. – Moe Nov 27 '20 at 13:03
  • http://coliru.stacked-crooked.com/a/05543b944ee23f2f I get 99 but I'm not entirely convinced this is legal one class is a POD the other isn't (also not related but you're leaking the result of that malloc call) – Borgleader Nov 27 '20 at 13:03
  • 1
    if you are actually using both C and C++ then please clarify in the question. Most of the time the two are incorrectly tagged together when only one is appropriate – 463035818_is_not_an_ai Nov 27 '20 at 13:04
  • 1
    I am pretty sure it's UB because those aren't compatible types, but I'm struggling to prove it. – Bartek Banachewicz Nov 27 '20 at 13:06
  • [tio.run](https://tio.run/##bZDBTsMwEETP9VesipDsiKrlWEJ74QM4cAQOru1Qo8SxYqeoVOHXwzp2IlLhy0azM37jCGtXouTmo@9vtBFlKxU86tr5RvFqTwjunIPq/BTmhQAe2x5KLR7IIqmUXbphsfi5VsIRR96EmUnueT7J2vhhnniTky4nBJGt8Ih6wQ@IqJj9k0ypMeTPVklVwDwbB14Z3BXXhrJ0X@oH2fCs58Mn7MCoL5h6R8q4Xe0DOXlCl9f7zfu15RQq7mC7RWCCDC2yUCoiaJQyVvGyrAV1@lvVBZ21Zon9T2qCRYdt8F0FXY7WWOFWwptZ3s1ElsN6Df6oXQzBBn@g84pLqAtsTLq@/wU) can run it with clang as your expect it. But still maybe UB – Ackdari Nov 27 '20 at 13:08
  • 1
    Structs can be used as base classes, so use myStru as a base class for myClass. – QuentinUK Nov 27 '20 at 13:08
  • @Ackdari why not? Is there any problem of allocating a pointer to `myStru`? FYI I do delete any allocated memory later in my codes – Moe Nov 27 '20 at 13:09
  • 1
    First of all this is not [SSCCE](http://sscce.org), please provide a piece of code that will compile. Second, class members are by default private, so `classObj->data = new char[10];` is not a valid operation. Using `malloc` is bad, stick to `smart pointers` if you have to allocate objects on heap, you already have two memory leaks. Finally, you can't cast between unrelated objects even if their signatures "match". Either provide specialized constructor or use `reinterpret_cast`. Keep in mind that `reinterpret_cast` is a risky solution. – NRUB Nov 27 '20 at 13:11
  • @MichałKaczorowski I don't think `reinterpret_cast` is legal here. This question could potentially be closed as a dupe (or at least is closely related to) https://stackoverflow.com/questions/21956354/can-i-legally-reinterpret-cast-between-layout-compatible-standard-layout-types – Bartek Banachewicz Nov 27 '20 at 13:12
  • @MichałKaczorowski I've updated the question to comply with SSCCEE. – Moe Nov 27 '20 at 13:18
  • @BartekBanachewicz I'm trying to cast in `C` not `C++` so I can't use `reinterpret_cast` please see the updates on my qestion. – Moe Nov 27 '20 at 13:20
  • 2
    AFAICT, there is no well-defined way to cast between those (and access them afterwards). The proper thing to do is to define the `myStru` struct and derive `myClass` from it. (Also, your "C" example is in C++ and has a memory leak.) – molbdnilo Nov 27 '20 at 13:22
  • 1
    Are you sure it printing 0 instead of 99? The link you attached is showing 99. One thing your memory allocation of struObj (through malloc) is of no use & not-requited to do, as you re-assign that variable to another pointer( i,e, pointer to class object) in next line. – gobinda Nov 27 '20 at 13:31
  • @gobinda you are right, it prints 99 (updated my question). I'm still unsure if what I'm doing by this casting class obj to struct obj is right in the way I do it? – Moe Nov 27 '20 at 13:37

2 Answers2

3

I am not 100% sure, because it's C++ and you can never be, but I think this is undefined behaviour.

You cannot access the members of a struct via a pointer to a member to another struct. A notable exception of that rule involves accessing the common initial sequence of two structs that are members of a union.

This could work if you actually inherited your class from the struct instead.

Alternatively, the other exception is that you can safely memcpy between such structs, AFAIR.

Bartek Banachewicz
  • 38,596
  • 7
  • 91
  • 135
  • thanks for your answer. I actually find a good answer to my question here: https://stackoverflow.com/questions/2203023/how-to-call-a-c-class-and-its-method-from-a-c-file. Also, this article (see Accessing C++ Classes from C ) has a good answer: https://www.oracle.com/technical-resources/articles/it-infrastructure/mixing-c-and-cplusplus.html – Moe Nov 27 '20 at 16:06
1

This question is about inter-operability and you'll never get a definitive answer because it relies on implementation defined aspects of the C and C++ compiler. The struct and the class are layout-compatible types in C++ terms. It's very clear that declaring one a class and one a struct isn't an issue and because they both have the same member-access for all their members (though different from each other) are compatible (at least in C++14 onwards I believe).

The best the standard is going to offer is:

Note: Standard-layout classes are useful for communicating with code written in other programming languages. Their layout is specified in 12.2. — end note

The intention is that such classes (which are both standard-layout and layout-compatible) be inter-operable. If you're using the same compiler in two modes then it would 'disappointing' if this didn't work. But that's the best you'll get 'disappointing' - check the documentation.

I do wonder a bit about the resource managementin the overall strategy. You've declared a destructor in the C++ class and that's fine it's not virtual!), but you copy it into a struct on the C side, if the C code has a 'destructor like' function to destroy its objects that may lead to trouble because you've bitwise cloned the C++ object (hopefully!).

If C called free(s->data) on its copy of an object constructed in C++ that had used new you're provoking Undefined Behaviour. That might be the except case that warrants using malloc() on the C++ side for compatibility.

SO it's probably OK, but you're relying on inter-operability capabilities between C and C++ and the two standards take you as far as it can but by definition you're using implementation defined features.

By that note we know the standard is willing it to work and it probably will. Good luck!

Persixty
  • 8,165
  • 2
  • 13
  • 35