开发者

Forward declarations of unnamed struct

开发者 https://www.devze.com 2023-04-02 00:20 出处:网络
Bounty question: So, these two Foos aren\'t the same thing. Fine. The second form is given in a library. How do I forward-declare it given that I can\'t change it?

Bounty question: So, these two Foos aren't the same thing. Fine. The second form is given in a library. How do I forward-declare it given that I can't change it?


I always thought C and C++ allowed repeated declarations provided that there were no repeated definitions. Then I came across this problem when trying to write C++ code which extends a C library.

struct Foo;
typedef struct {} Foo;

This gives the following error:

'struct Foo' has a previous declaratio开发者_如何学运维n as 'struct Foo'

I want to forward-declare, darn it! What's wrong here?


typedef-ing anonymous struct is a practice that pre-dates C++03 and is mainly oriented to retain compatibility with pre-C99 compilers.

Given that this is 2011, and that both C++ and C are changed, I wonder why there is no more up-to-date version of such a library!

If it is not in development anymore, you cannot "leave", but just "survive" and change it is the way to do that. If still in deployment, submit the issue to the development team.

If you need a workaround, consider that struct can inherit. So, write a forward declaration like

struct MyFoo;

and define it as

#include "old_library.h"
struct MyFoo: public Foo {};

And in all your code, forget about Foo and always use MyFoo.


You're declaring two different entities with the same name. The first, struct Foo, is a struct named Foo. The second is an alias for an anonymous struct.

If you do instead:

struct Foo;
struct Foo {};

It works, because you're declaring a struct named Foo in both situations.

You cannot forward declare anonymous structs. You're left with two choices: include the whole definition, or change the header and name the struct.


In a similar situation, I have a legacy C header with something like

== old_library.h ==
typedef struct { int data; } Foo;
== /old_library.h ==

I use it in a C++ class of my own, as parameter for a private method:

class C {
  void run(Foo *ds);
  ...
}

To avoid #include "old_library.h" from C.hpp, I use the following forward declaration:

class C {
  struct Foo;
  void run(Foo *ds);
  ...
}

and in C.cpp I have the following statements:

extern "C" {
#include "old_library.h"
}
struct C::Foo : public ::Foo {};

This way, I use C::Foo instead of Foo transparently, and don't need a MyFoo!


You don't need to typedef structs in C++:

struct Foo;     // Forward declaration

struct Foo 
{

}; // Definition

If you want to call it just Foo instead of struct Foo in C, you do need the typedef, which can also be done in different ways:

struct Foo;     /* Forward declaration */

struct Foo /* The name is needed here */
{

}; /* Definition */
typedef struct Foo Foo;  /* typedef */

or

struct Foo;     /* Forward declaration */

typedef struct Foo /* The name is needed here */
{

} Foo; /* Definition and typedef combined */

You can of course use the form struct Foo in both C and C++.


Your forward declaration declares that there will be a struct called Foo.

Your second declaration is of a typedef called Foo. These are not the same thing.


For a forward declaration of a typedef, you need to refer to the thing that is being typedeffed, so like:

struct foo;
typedef foo bar;
class foo{};

Since you want to forward declare an anonymous struct, you can neither give it a name in the forward declaration of the original entity, nor can you refer to it when typedefing it. The "logical" syntax would be:

struct ;
typedef bar;
class {};

But since this is obviously not possible, you can not forward declare anonymous structs.

To go standardese, lets have a look at 9.1-2:

A declaration consisting solely of class-key identifier; is either a redeclaration of the name in the current scope or a forward declaration of the identifier as a class name. It introduces the class name into the current scope.

No identifier, no forward declaration.

Bottom line of this: avoid anonymous structs unless they give you an advantage that you really need.


Your specific compiler may make a difference here.

Using MinGW GCC 3.4.5, both declarations compile with no errors or warnings (using -Wall)

struct Foo;
typedef struct {} Foo;

and

struct Foo;
typedef struct Foo {} Foo;

Is it possible a forward-declaration already exists inside the library? For instance, to allow a circular pointer:

struct Foo;
typedef struct Foo {
    struct Foo *fooPtr;
} Foo;

If this already exists within the library headers, it would cause the error you describe.


IMHO, simply change your typedef to,

typedef struct Foo {} Foo;
              ^^^^^

There is no harm and it will still compatible in both C & C++. Now you can forward declare it.

[Note: If you still insist on on not touching the typedef at all then here is the dirty trick.

struct Foo;
#define struct struct Foo
#include"Foo.h"  // contains typedef struct {} Foo;
#undef struct

This will work, only if Foo.h contains only 1 struct declaration. I don't recommend it.]


I think I recently ran into the same problem as the original poster, and have resolved it as below:

I am writing a wrapper around a third-party provided API defined as:

Foo.h:

typedef struct _foo 
{
    int i;
} Foo;

Foo* MakeFoo();
int UseFoo(Foo* foo);

My wrapper needs to have Foo as a member, but I don't want to expose Foo to all consumers of my wrapper.

UberFoo.h:

#pragma once

struct _foo;  // forward declare the actual type _foo, not the typedef Foo

class UberFoo
{
public:
    UberFoo();
    int GetAnswer();
private:
    _foo* f;
};

UberFoo.cpp:

#include "UberFoo.h"
#include "Foo.h"

UberFoo::UberFoo()
{
    this->f = MakeFoo();
}

int UberFoo::GetAnswer()
{
    return UseFoo(f);
}

Now, the consumers of my class can instantiate it without having access to the actual definition of _foo/Foo. This would also work if I needed to pass pointers to _foo as parameters or return them from functions as well as having a member _foo.

main.cpp:

#include <cstdio>
#include "UberFoo.h"

int main(int argc, char* argv[])
{
    UberFoo u;
    printf( "The Answer = %d\n", u.GetAnswer());
    return 0;
}

The trick here was to forward declare the actual struct type, not the typedef'd name. Note that it is critical that the struct not be anonymous, as is common in older C code:

typedef struct // the actual struct type underlying the typedef is anonymous here
{
    int i;
} ThisWontWork;

Hope this helps!


You can't forward declare it, since its unnamed. Its an unnamed struct, for which Foo is a typedef.


Why don't you just avoid forward decl.

If the second definition was in a header file, you could include the header file first in your C++ header file. To make C++ think it as C header, embracing #include with extern "C" { and } must be suffice in most case.


You are trying to typedef a previously used name. The statement is perfectly valid, only you need to use a different name.

struct Foo; // Forward declaration of struct Foo
typedef struct {} anotherFoo; // Another structure typedefed
struct Foo {
 // Variables
}; // Definition of the forward declared Foo.

Note that the typedef cannot be used in the same name.

0

精彩评论

暂无评论...
验证码 换一张
取 消

关注公众号