17

In Mixing C and C++ Code in the Same Program the following example (slightly abbreviated here to the relevant parts) is given. Assume buf.h contains the following:

struct buf {
    char* data;
    unsigned count;
};

// some declarations of existing C functions for handling buf...

It is then recommended to use

extern "C" {
  #include "buf.h"
}

class mybuf : public buf {
public:
    mybuf() : data(0), count(0) { }

    // add new methods here (e.g. wrappers for existing C functions)...
};

in order to use the struct within C++ with added features.

However, this clearly will produce the following error:

error: class `mybuf' does not have any field named `data'
error: class `mybuf' does not have any field named `count'

The reasons for this are explained in How can I initialize base class member variables in derived class constructor?, C++: Initialization of inherited field, and Initialize parent's protected members with initialization list (C++).

Thus, I have the following two questions:

  1. Is the code provided just plainly wrong or am I missing some relevant aspect? (After all, the article seems to stem from a reputable source)
  2. What is the correct way to achieve the desired effect (i.e., turning a C struct into a C++ class and adding some convenience methods like, e.g., a constructor, etc.)?

Update: Using aggregation initialization as suggested, i.e.,

mybuf() : buf{0, 0} {}

works, but requires C++11. I therefore add the following question:

  1. Using C++03, is there a better way to achieve the desired outcome than using the following constructor?

    mybuf() {
      data = 0;
      count = 0;
    }
    
Community
  • 1
  • 1
godfatherofpolka
  • 1,645
  • 1
  • 11
  • 24
  • 1
    Looks like a copy&paste bug. Earlier on the same page, the author implemented a class `mybuf` having `data` and `count` members. – dyp Apr 20 '15 at 10:48
  • 1
    why do you put a extern "C" around your struct? there is no need. – AndersK Apr 20 '15 at 10:56
  • 1
    @CyberSpock See the original source of the code, this is about mixing C and C++ code and the header file contains some function declarations, I've edited the code above to make this clear. – godfatherofpolka Apr 20 '15 at 11:24
  • @godfatherofpolka a struct is a struct whether it is C or C++. there is no reason to wrap it in an extern "C" block. Your question is not about mixing C-C++ it is about initializing member variables in the base class. – AndersK Apr 21 '15 at 06:06
  • @CyberSpock Yes, you're right, the extern "C" is not relevant to the problem, but I kept it there to provide some context of the question, in particular to highlight that the struct is given as a pure C struct and initialization can thus not happen on the struct level (which would be the obvious answer in case it was a C++ struct). – godfatherofpolka Apr 21 '15 at 09:21
  • @godfatherofpolka personally i would do composition instead, then it would be more self-contained where the class instance could take ownership of the contents of buf. But it depends on how you want to use it. – AndersK Apr 21 '15 at 11:26

4 Answers4

8

If you can use a c++11 compatible compiler then this would be a perfect use case for an initializer list using aggregate initialization.

mybuf() : buf{0, 0}
{}
Simon Gibbons
  • 6,969
  • 1
  • 21
  • 34
3

One "correct" way, if your compiler is C++11 capable, is to use e.g.

mybuf() : buf{0, 0} {}
Some programmer dude
  • 400,186
  • 35
  • 402
  • 621
3

This has nothing to do with mixing C and C++. You're trying to initialise members that don't exist; that they exist in a base class isn't enough. You need to initialise the base itself.

In this case, use aggregate initialisation:

class mybuf : public buf
{
public:
    mybuf() : buf{0, 0} {}
};
Lightness Races in Orbit
  • 378,754
  • 76
  • 643
  • 1,055
2
class mybuf : public buf {
public:
    mybuf();    
    // add new methods here (e.g. wrappers for existing C functions)...
};

const buf init = {0,0};

mybuf::mybuf() : buf(init) {};

will work.

I have seen this work with some compilers, but don't have a standard handy to check if it is standard or an extension.

class mybuf : public buf {
public:
    mybuf() : buf(init) { }

    // add new methods here (e.g. wrappers for existing C functions)...

    private:

    const buf init = {0,0};
};
Peter
  • 35,646
  • 4
  • 32
  • 74
  • As to your second method: GCC 4.9 [accepts it](http://goo.gl/ixNWEG), CLang 3.6rc2 [gives a `-Wuninitialized` warning](http://goo.gl/yUxcF9) on the constructor line, ICC 13 [rejects it](http://goo.gl/GnHkY6) with an error 409 on the constructor line, and MSVC 17 (VS2012) rejects the initializer with a C2059 'unexpected {' error followed by C2334 'unexpected token(s) preceding '{'' and then a C2065 'undeclared identifier' at the constructor definition. I'd say it's either a GCCism/GCC extension, or UB. – LThode Apr 20 '15 at 13:15