1

I have this line in a header file:

typedef struct mystruct *mystruct;

And the corresponding struct definition in a .c file. Pretty standard practices.

I am getting this compilation error:

fatal error: typedef redefinition with different types ('struct mystruct *' vs mystruct')

This is using the Hexagon Tools Compiler (7.2.12) from Hexagon 3.0 SDK. It is officially QuIC LLVM Hexagon Clang version 7.2.12. Building for Snapdragon Flight. This should work as far as I know. It works with Ubuntu clang version 3.5.0-4ubuntu2~trusty2 (based on LLVM 3.5.0) for x86_64-pc-linux-gnu.

What is wrong here? Is this type of typedef a newer feature of C that is not implemented in the compiler, or rather are compiler differences like these common?

Edit: Actually struct is defined in a .c, not .cpp, file. Added the Makefile and make output showing compilation with Ubuntu clang, as well as the top of the header file with the troublesome typedef statment. A test is run at the end, and all 105 tests pass.

Edit2: See Jonathan Leffler's answer for cases where this works vs doesn't work.

ringbuf.h:

#include <stddef.h>
#include <sys/types.h>

#define MIN(a,b) (((a)<(b))?(a):(b))
#define MAX(a,b) (((a)>(b))?(a):(b))

typedef struct ringbuf_t *ringbuf_t;

Makefile:

CC=clang
CFLAGS=-O0 -g -Wall -Wpointer-arith -ftrapv -fsanitize=undefined-trap -fsanitize-undefined-trap-on-error

# or, for gcc...
#CC=gcc
#CFLAGS=-O0 -g -Wall

LD=$(CC)
LDFLAGS=-g

test:   ringbuf-test
    ./ringbuf-test

coverage: ringbuf-test-gcov
      ./ringbuf-test-gcov
      gcov -o ringbuf-gcov.o ringbuf.c

valgrind: ringbuf-test
      valgrind ./ringbuf-test

help:
    @echo "Targets:"
    @echo
    @echo "test  - build and run ringbuf unit tests."
    @echo "coverage - use gcov to check test coverage of ringbuf.c."
    @echo "valgrind - use valgrind to check for memory leaks."
    @echo "clean - remove all targets."
    @echo "help  - this message."

ringbuf-test-gcov: ringbuf-test-gcov.o ringbuf-gcov.o
    gcc -o ringbuf-test-gcov --coverage $^

ringbuf-test-gcov.o: ringbuf-test.c ringbuf.h
    gcc -c $< -o $@

ringbuf-gcov.o: ringbuf.c ringbuf.h
    gcc --coverage -c $< -o $@

ringbuf-test: ringbuf-test.o libringbuf.so
    $(LD) -o ringbuf-test $(LDFLAGS) $^ -L$(MY_LIBS_PATH) -lringbuf

ringbuf-test.o: ringbuf-test.c ringbuf.h
    $(CC) $(CFLAGS) -c $< -o $@ 

libringbuf.so: ringbuf.o
    $(CC) -shared -o libringbuf.so ringbuf.o
    cp ./libringbuf.so $(MY_LIBS_PATH)/

ringbuf.o: ringbuf.c ringbuf.h
    $(CC) $(CFLAGS) -fPIC -c $< -o $@
    cp ./ringbuf.h $(MY_INCLUDES_PATH)/

clean:
    rm -f ringbuf-test ringbuf-test-gcov *.o *.so *.gcov *.gcda *.gcno

.PHONY: clean

make output:

clang -O0 -g -Wall -Wpointer-arith -ftrapv -fsanitize=undefined-trap -fsanitize-undefined-trap-on-error -c ringbuf-test.c -o ringbuf-test.o 
clang -O0 -g -Wall -Wpointer-arith -ftrapv -fsanitize=undefined-trap -fsanitize-undefined-trap-on-error -fPIC -c ringbuf.c -o ringbuf.o
cp ./ringbuf.h /home/eric/Includes/
clang -shared -o libringbuf.so ringbuf.o
cp ./libringbuf.so /home/eric/Libs/
clang -o ringbuf-test -g ringbuf-test.o libringbuf.so -L/home/eric/Libs -lringbuf
./ringbuf-test

Edit3: This actually works fine with just the Hexagon-clang compiler. It is the compilation process of the larger program that this module exists in that is being problematic. I think that it is attempting to compile this code as C++.

errolflynn
  • 641
  • 2
  • 11
  • 24
  • You are reusing the name `mystruct`. Why would you expect this to work? – Code-Apprentice Feb 17 '17 at 02:51
  • 2
    This code would be illegal in C++. You say "a .cpp file" in your question. But you tagged the question as C. C and C++ are different languages. Please decide which language you are using... – M.M Feb 17 '17 at 03:20
  • 2
    Also, it is not even standard practice in C to use the same name for a struct tag, and for a pointer to that type. When people do use pointer typedefs (which is generally discouraged), they tend to use some sort of hungarian prefix for the pointer typedef (e.g. `pmystruct`). – M.M Feb 17 '17 at 03:20
  • My mistake, corrected to .c file. I didn't write this code for reference. Got it from `https://github.com/dhess/c-ringbuf`. It won't be too difficult to change the struct typedef if necessary. However, this definitely works with another compiler. – errolflynn Feb 17 '17 at 19:16
  • See Jonathan Leffler's comment that this works b/c of the namespaces surrounding (struct, union, enum) tags in C. Albeit not with the Hexagon clang compiler – errolflynn Feb 17 '17 at 19:35

1 Answers1

2

Your code would be fine, in C and C++, if you did not try to Use typedef for a pointer type.

The header (hdr.h for instance) could/should contain:

typedef struct mystruct mystruct;

The source (hdr.cpp for instance) could contain:

#include "hdr.h"

struct mystruct
{
    const char *a;
    int   b;
    int   c;
};

#include <iostream>

int main()
{
    mystruct *ap = new mystruct;
    ap->a = "collywobbles";
    ap->b = 1;
    ap->c = 2;
    std::cout << "a: " << ap->a << ", b = " << ap->b << ", c = " << ap->c << "\n";
    return 0;
}

This will compile in C++, even under stringent warnings. An equivalent C main() using <stdio.h> would work in C.

Community
  • 1
  • 1
Jonathan Leffler
  • 730,956
  • 141
  • 904
  • 1,278
  • Why does this work in .c but not .cpp – errolflynn Feb 17 '17 at 19:17
  • 1
    As shown in the answer, with no pointer in the `typedef`, the code works fine in C and C++. If you try to include the pointer in the `typedef`, it won't work in C++, but (come to think of it) it would work, albeit confusingly, in C because the (struct, union, enum) tag namespace used for `struct mystruct` is separate from the 'ordinary identifiers' namespace used for typedef names (and variable names). – Jonathan Leffler Feb 17 '17 at 19:19
  • It is compiling for me with Ubuntu clang. I have posted the `Makefile` and the make output. – errolflynn Feb 17 '17 at 19:20
  • I've updated my comment; it does work in pure C (see previous comment). It's edge-case territory — a dubious design in C, though not actually illegal. It is actively not allowed in C++ — one reason for regarding it with scepticism in C. – Jonathan Leffler Feb 17 '17 at 19:24
  • That's funky. Would it then work if included in C++ code as a .so or .a library? – errolflynn Feb 17 '17 at 19:28
  • I do not think that this question is a duplicate by the way. – errolflynn Feb 17 '17 at 19:29
  • 1
    If you mean, could you link a C library using the typedef with pointer to a program otherwise using C++, the answer is "Yes" because the C functions would be declared `extern "C"` so that they can be called from C++, and that omits the typesafe linkage information that would otherwise mess things up. – Jonathan Leffler Feb 17 '17 at 19:30
  • Regarding duplicacy or not — it is very much in the same territory, but maybe not perfect. I'm OK with unduplicating it (I won't fight it), but I probably won't cancel it myself. – Jonathan Leffler Feb 17 '17 at 19:32
  • ^^ Then the Hexagon clang compiler does not support this behavior in a .c file far as I can tell. – errolflynn Feb 17 '17 at 19:33
  • 1
    That I cannot test, not having access to a Hexagonal (or Pentagonal, or …) version of `clang`. I'll double check what Mac `clang` does. – Jonathan Leffler Feb 17 '17 at 19:34
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/135996/discussion-between-jonathan-leffler-and-errolflynn). – Jonathan Leffler Feb 17 '17 at 19:38
  • 1
    Why not update the answer instead of just the comments? This answer is misleading: whether doing this is a good idea or not is not the subject of the question (for that matter, that also means this is definitely *not* a duplicate, although it might be a "typographical or other" close candidate instead). – Alex Celeste Feb 17 '17 at 21:53