46

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 declaration as 'struct Foo'

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

R. Martinho Fernandes
  • 228,013
  • 71
  • 433
  • 510
spraff
  • 32,570
  • 22
  • 121
  • 229
  • 1
    What's the compiler? Also, maybe the empty declaration is bothering it? – Eli Iser Aug 31 '11 at 11:45
  • 9
    There's no language called "C/C++", *especially* in the context of structs. There are too many alleged C++ writers to litter "struct" over their code like it was 1989. – Kerrek SB Aug 31 '11 at 11:51
  • 2
    @Kerrek there's nothing wrong with `struct` in C++ code, I use it to indicate I am writing an unencapsulated POD type. – spraff Aug 31 '11 at 11:56
  • 2
    @spraff: I think Kerrek is referring to the C "idiom" of not hiding `struct`s behind a `typedef`, instead doing things like void `foo(struct MyStruct *p);`. – Oliver Charlesworth Aug 31 '11 at 11:58
  • @Oli: Exactly. And things like typedef definitions, `typedef struct Foo { /* ... /* } Foo;`. – Kerrek SB Aug 31 '11 at 12:22

12 Answers12

45

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.

Emilio Garavaglia
  • 20,229
  • 2
  • 46
  • 63
  • 3
    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! – Alex Cohn Jan 12 '12 at 10:00
  • "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!" Likely because the de facto standard compiler for C and C++ code on Windows, MSVC++, now in 2014 still does not support C99 and is unlikely to support it anytime soon. – Kaiserludi Feb 20 '14 at 17:29
  • @Kaiserludi Even in C99, if you just declared a `struct` in C++ style, then you have use keyword `struct` everywhere before its name in function prototypes, which `typedef` allows to avoid. – Swift - Friday Pie Sep 08 '22 at 10:54
40

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.

R. Martinho Fernandes
  • 228,013
  • 71
  • 433
  • 510
14

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!

Alex Cohn
  • 56,089
  • 9
  • 113
  • 307
8

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++.

molbdnilo
  • 64,751
  • 3
  • 43
  • 82
  • In C, you don't *need* the typedef; you can refer to the type as `struct Foo`. (Some of us prefer to do it that way.) – Keith Thompson Sep 07 '11 at 16:56
  • @KeithThompson: The separate typedef can also be done BEFORE the definition of the struct as a forward declaration. – Devolus Aug 24 '15 at 06:36
5

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.

Oliver Charlesworth
  • 267,707
  • 33
  • 569
  • 680
2

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.

PlasmaHH
  • 15,673
  • 5
  • 44
  • 57
  • 2
    Sometimes the anonymous structs come with legacy headers, and it's a bad idea to mess with some old 3rd party library headers. The solution dates back to 2001 [http://groups.google.com/group/comp.lang.c++.moderated/browse_thread/thread/4f1aa831b91266b/96fcc9007e24c662]: you cannot forward declare from an unnamed struct "as is", but you can inherit from it, and forward declare your derived class! – Alex Cohn Jan 12 '12 at 10:10
2

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.

Unsigned
  • 9,640
  • 4
  • 43
  • 72
1

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.]

iammilind
  • 68,093
  • 33
  • 169
  • 336
  • I agree, with the minor change that I'd use: `typedef struct Foo_struct {} Foo;` which avoids name conflicts in some of the older C compilers and is generally more clear when trying to figure out weird compile errors (is it the struct or the typedef that's failing sorts of things, C++ compilers don't have this issue but I think it's still considered good form). – Kevin Williams Sep 06 '11 at 18:59
1

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!

Ken Griggs
  • 11
  • 1
0

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.

shr
  • 525
  • 4
  • 7
0

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

K-ballo
  • 80,396
  • 20
  • 159
  • 169
  • 1
    The idea dates back to 2001 [http://groups.google.com/group/comp.lang.c++.moderated/browse_thread/thread/4f1aa831b91266b/96fcc9007e24c662]: you cannot forward declare from an unnamed struct "as is", but you can inherit from it, and forward declare your derived class! – Alex Cohn Jan 12 '12 at 10:07
0

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.

jagbandhuster
  • 677
  • 2
  • 7
  • 20