1

After reading

  1. possible to define a function inside a C structure? [duplicate]
  2. Define functions in structs

I wonder if the following workaround would work and what can be its deepest implications at compiler-level.

// foo.h
typedef struct foo
{
    int i;
#ifdef __cplusplus
    foo(int _i) : i(_i) {};
#endif
} foo;

// bar.c
#include "foo.h"
foo bar;
bar.i = 1;

// bar.cpp
#include "foo.h"
foo bar(2);

In my view is a great trick to leverage "struct polymorphism", being able to keep portability with C legacy code, but I have the feeling that in some way I'm now working with two different types.

Patrizio Bertoni
  • 2,582
  • 31
  • 43
  • 1
    You're also working with two different languages...what are you trying to achieve? – Chris Turner Aug 01 '17 at 08:51
  • 1
    If you compile with a C compiler, you don't have C++ function in the struct. And if you compile with a C++ compiler, you do not have a C struct. Yes, you have 2 completely different types. – Gerhardh Aug 01 '17 at 08:53
  • The environment is a own C++11 library which depends, among other libs, from an old C89 library. Some c files of mine are clients of this lib and are C11 compiled; the example struct would be largely shared over my library, and I'd like to be sure it would be of the *exactly* same type. – Patrizio Bertoni Aug 01 '17 at 08:55
  • 4
    I think this probably breaks at last when you use C++ feature that introduces a vtable to your structure. – user694733 Aug 01 '17 at 08:58
  • **JUST NO**. Seriously :-P – Nikos C. Aug 01 '17 at 08:59
  • 1
    There is no "workaround", C++ is not C, so the two compilers will produce completely different outputs. If you are asking whether the two structs will have the same *layout* in memory, I would recommend that you use packing directives to ensure this yourself. Regarding the "struct polymorphism", I don't even know what this is supposed to mean in C. – vgru Aug 01 '17 at 09:00
  • *Whyyyyy?* Seriously. If this is only to add a constructor, you don't need to. Brace initialization `foo f = {2}` will work in C AND C++. – StoryTeller - Unslander Monica Aug 01 '17 at 10:48
  • hide your data type using opaque(abstract) data type: https://stackoverflow.com/questions/2001637/opaque-abstract-data-types-in-c – sailfish009 Aug 01 '17 at 11:04
  • No Storyteller, that was just a MWE – Patrizio Bertoni Aug 01 '17 at 11:37

3 Answers3

4

Looking at this struct definition from your question:

typedef struct foo
{
    int i;
#ifdef __cplusplus
    foo(int _i) : i(_i) {};
#endif
} foo;

It is likely to result in the same memory representation when compiled with a C and a C++ compiler targeting the same platform. But you should never rely on this. The type the C++ compiler sees has one member more than the type the C compiler sees, so they are fundamentally different types. Therefore, treating them as the same is undefined behavior. If this works as you expect, it's by pure luck and might break at any time.

As already commented, introducing a virtual function is almost guaranteed to break it, as the C++ compiler will need to store the vtable somewhere in the struct. But even without that, there are many other ways in which it could break, for example because the compilers will add different padding. Just don't do this.

What you could do instead is use a plain C struct and add a wrapper class in C++ when you need it.

3

The two are distinct types. Compiling as C results in a different type than compiling as C++. Use both in the same program at the same time (i.e. linking the C and C++ objects together into one exectuable) and the result will be undefined behaviour, due to violation on the one-definition rule in C++.

This is not the way to keep portability between C and C++. The behaviour is undefined, so you might get lucky and find it works as you intend. But, equally, the behaviour is undefined, so it may not work.

Peter
  • 35,646
  • 4
  • 32
  • 74
2

If you want to ensure compatibility with a C library, this isn't the way.

You want to define a type that is-a foo and can be passed to the library functions. So do just that, inherit:

class shiny_foo : foo {
  // Member functions and anything that changes the object layout.
};

extern "C" void c_bar_func(foo *);

void cxx_bar_func(shiny_foo& obj) {
  // do stuff
  c_bar_func(&obj); // obj *is-a* foo as well, remember
}
StoryTeller - Unslander Monica
  • 165,132
  • 21
  • 377
  • 458