0

I want to create a program that uses a structure and functions around this structure for example a tree.

The interface would be something like :

typedef struct _trie *Trie;

void foo1(Trie trie);
int foo2(Trie trie);
...

I would like to create two implementations for this one that would be in the following files:

  • first_trie.c, first_trie.h
  • second_trie.c, second_trie.h

A third file who use the interface:

  • use_trie.c

The objective is to compile use_trie.c twice, once using second_trie.h and another using first_trie.h.

How to prevent use_trie from complaining about not knowing Trie? How to build a makefile that builds twice use_trie with those differents headers?

Neok
  • 221
  • 2
  • 17
  • If the two implementations have the same interface you only need one `.h` file. The compilation is done by file, so you only need to worry about the linking. – Tomas By Dec 30 '17 at 02:59
  • he will complain about not knowing _trie because it is in the other files – Neok Dec 30 '17 at 03:01
  • division for C and H files was material requirement on very poor machines in this era. Hsitorically lead to bad designed language parser – Jacek Cz Dec 30 '17 at 03:02
  • 1
    @Pixeuh: you need to design the interface so that `.h` file has everything the main file needs. – Tomas By Dec 30 '17 at 03:10
  • Ok, like getters and setters, I will try that ! – Neok Dec 30 '17 at 03:11
  • Works perfectly ! thanks :-) – Neok Dec 30 '17 at 03:49
  • 2
    Beware of using names starting with an underscore. [C11 §7.1.3 Reserved identifiers](http://port70.net/~nsz/c/c11/n1570.html#7.1.3) says: • _All identifiers that begin with an underscore and either an uppercase letter or another underscore are always reserved for any use._ — • _All identifiers that begin with an underscore are always reserved for use as identifiers with file scope in both the ordinary and tag name spaces._ You are treading on thin ice using `struct _trie`. You'll be in company with a lot of other people, though; people abuse these rules frequently. – Jonathan Leffler Dec 30 '17 at 04:26
  • 2
    BTW hiding pointerness in types is poor taste. I would prefer `typedef struct trie_st MyTrie;` then `void foo1(MyTrie* trie);` because you want to show pointer arguments in declaration of functions. – Basile Starynkevitch Dec 30 '17 at 04:42

2 Answers2

2

If the use_foo.c only uses the functions in the public interface that take pointers (and it never needs to dereference one of the pointers), it doesn't need to know about the internals. The type declaration is sufficient. This is called an opaque pointer.

Basile Starynkevitch
  • 223,805
  • 18
  • 296
  • 547
SoronelHaetir
  • 14,104
  • 1
  • 12
  • 23
2

You need a single header, trie.h, that is used by:

  • first_trie.c — so it can define functions matching the public interface defined in it.
  • second_trie.c — so it can define functions matching the public interface defined in it.
  • use_trie.c — so it can use the functions declared by the public interface defined in it.

Unless the trie code is so big that it needs to spill across multiple source files, you don't need (or want) first_trie.h and second_trie.h; the single, common definition in trie.h does the job. The internal details of the actual trie structures are kept entirely within the source files that implement each trie.

As noted in comments:

  • All identifiers that begin with an underscore and either an uppercase letter or another underscore are always reserved for any use.
  • All identifiers that begin with an underscore are always reserved for use as identifiers with file scope in both the ordinary and tag name spaces.

You are treading on thin ice using struct _trie. You'll be in company with a lot of other people, though; people abuse these rules frequently.

  • Be cautious about using typedef for pointers. See Is it a good idea to typedef pointers for a detailed discussion.

    Note that you should normally treat FILE as an opaque type, but you always declare FILE *fp etc.

You shouldn't have much problem with the makefile. A bare-bones outline could be as simple as:

TRIE1.c = first_trie.c
TRIE2.c = second_trie.c
PROG.c  = use_trie.c
TRIE1.o = ${TRIE1.c:.c=.o}
TRIE2.o = ${TRIE2.c:.c=.o}
PROG.o  = ${PROG.c:.c=.o}

PROG1 = use_trie1
PROG2 = use_trie2

all: ${PROG1} ${PROG2}

${PROG1}: ${PROG.o} ${TRIE1.o}
    ${CC} -o $@ ${CFLAGS} ${PROG.o} ${TRIE1.o} ${LDFLAGS} ${LDLIBS}

${PROG2}: ${PROG.o} ${TRIE2.o}
    ${CC} -o $@ ${CFLAGS} ${PROG.o} ${TRIE2.o} ${LDFLAGS} ${LDLIBS}

You'd probably want to add rules telling Make that the header is important too:

TRIE.h = trie.h

${TRIE1.o}: trie.h
${TRIE2.o}: trie.h
${PROG.o}:  trie.h

You don't necessarily need to add the source files to the dependency lists; it is complete, but not necessary, to do so.

Note that putting both first_trie.o and second_trie.o into a single library would be a bad idea. It would be indeterminate which of the two object files was linked with any given program. It needn't even be the same file that is linked consistently (though it probably would be). You will need separate libraries — or to keep the object files separate an link explicitly with the one you want to use.

Jonathan Leffler
  • 730,956
  • 141
  • 904
  • 1,278