2

I'm no C++ expect, as you will undoubtedly notice. But I'm trying to "upgrade" some old C code to C++. In the process, I'm trying to promote old structures to classes. I found this Oracle article about some of the things I'm trying to do.

http://www.oracle.com/technetwork/articles/servers-storage-dev/mixingcandcpluspluscode-305840.html

But 2 things. The article doesn't address a more complicated case I have. And my "workaround" doesn't seem to lead consistent results. So here I go with some details.

Old C-Style structure:

typedef struct
{
    int ia;
    float fa;
    ...
} Struct_A;

typedef struct
{
    Struct_A sA;
    int ib;
    float fb;
    ...
} Struct_B;

C++
According to the Oracle article, I should be able to promote Struct_A to a class and still be able to use the class version with old C functions. Here's apparently how:

class CStruct_A : public Struct_A  // Inherits from C- Struct_A
{
public:
    CStruct_A() : ia(0), fa(0.0f) {}
}

Pause. Right here, I already have an issue. I can't seem to initialize the C-Structure elements as indicated in the article and as shown above. The compiler doesn't accept it. I have to re-write the class as:

class CStruct_A : public Struct_A
{
public:
    CStruct_A()
    {
        ia = 0;
        fa = 0.0f;
    }
}

So that's the first question mark. Not a huge deal but the article claims it can be done.

Next, the tougher issue. Promoting the Struct_B, which contains a Struct_A member. I want to make sure Class A's constructor is used to initialize the variables. But I'm not sure how to force it. In my c++ code, if I have something like:

{
    CStruct_B *pCStrb = new CStruct_B();
    ...
}

Class B's constructor gets invoked but not class A's. The workaround I have is lame... But that's the only thing I can figure out for now. I declare an internal private function ini() in class A, make class B a friend and call init() from class B's constructor.

class CStruct_A : public Struct_A
{
    friend class CStruct_B;

public:
    CStruct_A() { init(); }

private:
    void init()
    {
        ia = 0;
        fa = 0.0f;
    }
}

class CStruct_B : public Struct_B
{

public:
    CStruct_B()
    {
        ib = 0;
        fb = 0.0f;
        static_cast<CStruct_A *>(&sA)->init();
    }
}

I have more questions but this is probably already too long.

Any help will be greatly appreciated.

ThermoX
  • 277
  • 3
  • 11
  • ` The compiler doesn't accept it` - it must be giving you a message - include that in your question... – John3136 Oct 08 '15 at 01:34
  • for CStruct_A initialization, you can't initialize members of a base class using the member initializer syntax. That only works for members of the current class. To initialize the base class you need to call the base class constructor or assign values to them in the body of the constructor like you're doing. Why are you creating new classes, instead of just modifying the existing structs? – 1201ProgramAlarm Oct 08 '15 at 01:42
  • @John. IntelliSense states (similar error with the compiler): Error: "ia" is not a nonstatic data member or base class of class "CStruct_A" – ThermoX Oct 08 '15 at 02:06
  • @ProgramAlarm I took the example pretty much "as is" from the Oracle example. Of course, the article may be wrong. (I've edited this part because my origial answer was incorrect) As for why the new classes? Because I want to use the extended features of my new classes for any new code I write... while the structures returned can still be used in the old code. – ThermoX Oct 08 '15 at 02:06
  • @John3136 Just tagging you properly. Sorry. I almost never ask questions. This site usually has all the answers I'm looking for. – ThermoX Oct 08 '15 at 02:17
  • @1201ProgramAlarm Just tagging you properly as well. – ThermoX Oct 08 '15 at 02:17

3 Answers3

1

I've skimmed through the "Oracle article" you referenced, and I find nothing in that article that suggests that you can initialize base classes in the manner you claim the article describes, or gives an example with anything anywhere close to the syntax you are trying to compile.

C++ classes' constructors can only initialize members of their class. They cannot initialize any members of their superclass directly from their constructor.

Having said that, some compilers might offer their own compiler-specific language extensions. For example, gcc 5.1 will accept the following code in -std=c++11 mode:

typedef struct
{
    int ia;
    float fa;
} Struct_A;

class CStruct_A : public Struct_A {

public:

CStruct_A() : Struct_A({
            .ia=1,
            .fa=0,
    })
    {
    }
};

But, that's horribly inefficient and is not standard C++ (as gcc will warn you in -Wpedantic mode).

Sam Varshavchik
  • 114,536
  • 5
  • 94
  • 148
  • In the Oracle article, look for the "Adding C++ Features to C Structs" section... Towards the bottom of that section, you'll see the "mybuf() : data(0), count(0) { }" line, where data & count are members of the C structure. That said... I can live with the extended form.. What now worries me... is that I'm starting to wonder *why* my workaround of calling init() from class b actually works!! As far as class b is concern, it doesn't know about a CStruct_A member. It knows about a Struct_A member but not the class version. Yet, it's able to call the CStruct_A' init() function... hmm.. – ThermoX Oct 08 '15 at 02:52
  • In the first example, the data and count referenced here are not members of the C structure. They are members of the class, declared after the private: keyword. C++ has forward declarations. In the example given, mybuf does not inherit from buf, at all. The second example is completely wrong. It's not standard C++. – Sam Varshavchik Oct 08 '15 at 03:13
  • I honestly can't comment too much on the validity of the second example in the link. But the article claims that it's actually the *right* way to declare it, to make sure to compiler aligns the bytes properly. The article states that the first example is not the safe way to do it. Of course, if you *do* use the first example... then yes, data and count are part of the class itself and there's no issue. – ThermoX Oct 08 '15 at 03:39
0

Alright...

To close this question, I guess the only way I found to initialize the sub-structure is pretty much to do what I did in my workaround. I haven't found another, direct way. I guess it isn't a big deal. I was just mislead by the original article.

Now, the old code has a global Struct_B defined as "extern" and my new c++ program must allocate it. To be able to use its promoted features while still be "defined" from the old C-code perspective, I declared it as:

{
    Struct_B *pCStrb = new CStruct_B();
    ...
}

So I declare it as the old c-structure but allocate it with the class version. This is different than what I had originally written above, where I used the "C++" version to declare it. That was obviously wrong as the old C-code doesn't know anything about a structure called CStruct_B.

Anyway... seems to be doing what I wanted, albeit not exactly "as" I wanted.

Thanks for the help.

ThermoX
  • 277
  • 3
  • 11
-1

To address your first issue. From a C++ perspective, these C structs are considered aggregates. So you can do:

class CStruct_A : public Struct_A
{
public:
  CStruct_A() : Struct_A{0, 0.0f} {};
}

This works perfectly for me. The braces initializer is just initializing Struct_A in the order of its members.

The best that I can see to deal with your second issue is to give CStruct_A an implicit conversion back to Struct_A, and use that.

struct A {
  double x;
  int y;
};

class Ac : public A {
public:
  Ac() : A{0.0, 1} {};

  operator A() const { return A{x, y}; }
};

struct B {
  B() : a(Ac()) {};

  A a;
};

Edit: all that said, I'm not really sure that inheritance is the first place I'd look to solve this problem. I'd almost certainly rather start by encapsulating the C style structs in C++ classes and go from there.

To elaborate how I would do this:

class CStruct_A {
// ... various other code, initializes a carefully etc
public:
  Struct_A * getA() { return &a; }
private:
  Struct_A a;
}

Now, given some function:

void func(Struct_A * var);

Call it like this:

CStruct_A my_var();
func(my_var.getA());

It may not be as fancy as using inheritance, but it's simple, it works, it allows you to fully encapsulate the C structs and decide how they are initialized, the compiler will protect you against accidentally calling func(myvar), etc. I'd prefer this over inheritance unless there's a more compelling reason to use inheritance than you outlined.

Nir Friedman
  • 17,108
  • 2
  • 44
  • 72
  • 1
    hmm. Doesn't work for me. I get: error C2059: syntax error : '{' error C2334: unexpected token(s) preceding '{'; skipping apparent function body. I even cut & pasted your code, with the same results. As for the second part of your answer... I'll have to chew on that a little.. Lots of new concepts for me right now... But aren't you implying to modify the original struct B? Finally... Some clarification is probably needed. In the old code, a pointer of "struct a" is declared and is used "globally" throughout the library. But the app using the library has to allocate it. – ThermoX Oct 08 '15 at 03:27
  • So the C-library has to be able to use that structure. However, in my C++ app, I want to allocate it using the C++ version and use the class' extended features within that app... while keeping the old C-library happy. – ThermoX Oct 08 '15 at 03:29
  • What version of C++? Edit: I suppose you are using 03, rather than 11. You should probably specify that; it's a good idea generally and I think in particular by now a lot of people are thinking in 11 these days when they see a random online question. – Nir Friedman Oct 08 '15 at 03:42
  • I'm using Visual Studio 2015, but using the Platform Toolset from 2010. – ThermoX Oct 08 '15 at 03:47
  • I wasn't sure if you were talking about the version's standard or which compiler I was using. The standard is probably more important. But I'm not sure how to get the standard studio uses. I'm Googling it up now. – ThermoX Oct 08 '15 at 03:55
  • I just read your extended explanation. That's what I thought you meant. But the old code is truly horrific. A more detailed explanation is *still* probably needed. Say the old library has a function called: olfdunc(). Now, this old function does not accept parameters. However, that old function "expects" a globally declared "struct a" to be defined, for its code to work. Therefore, that globally defined "struct a" has to be C standard. Using a derived class, the new class cstruct_a can be used "as is" within the new code, as well as the old code. – ThermoX Oct 08 '15 at 04:09
  • Even a partially upgraded project, which would contain both c code AND c++ code, could use that same globally declared structure. – ThermoX Oct 08 '15 at 04:09