4

I want to define an API function in header file, and then implement it in source file.

Here is the hello.h, I want to define "world" in header:

typedef struct Hello {
    int (*world) (void);
} Hello;

Here is the hello.c, I want to implement "world" in source:

#include "hello.h"

struct Hello {
    int foo;
    int bar;
    int other;
    int fields;
    int many;
    int private;
    int only;
    int in_source_file;
    
    int (*world) (void);
};

Hello* hello = malloc(sizeof(Hello));
hello->foo = 1;

Here is the error in vscode:

struct "Hello" has no field "foo".

Is it possible in c? How to do if yes?

4
  • 2
    you have two different definitions of Hello, this is not possible if the c file includes the header (and even apart of that the code you give cannot be the code you compiled). I want to implement "world" in source : it seems you confuse implementation and initialization, only a function can be implemented in C Commented Sep 25 at 8:10
  • How you come to struct "Hello" has no field "foo"? GCC is saying error: redefinition of struct or union 'struct Hello' Commented Sep 25 at 23:38
  • @3CEZVQ, the VSCode didn't prompt for redefinition error, it only prompts for struct "Hello" has no field "foo", I'm stilling writing code without compiling in VSCode. Commented Sep 26 at 3:33
  • Search term you are looking for is "pimpl", short for "pointer to implementation" Commented Sep 27 at 7:12

7 Answers 7

7

as I said in a comment of your question :

you have two different definitions of Hello, this is not possible if the c file includes the header

and

I want to implement "world" in source

it seems you confuse implementation and initialization, only a function can be implemented in C


How to define API in header and private fields in source in c

If your goal is to hide some part of the definition to not allow a code including the header to use them, you can use a void * attribute and initialize/use it in your source.

Example in the header file :

/* ... */

typedef struct {
   void * internal;
   /* declare some 'public' fields as world if they exist */
} MyStruct;

MyStruct * createMyStruct();    /* malloc and init */
void freeMyStruct(MyStruct *);  /* purge and free */

/* to allow array of MyStruct */
void initializeMyStruct(MyStruct *); /* init only */
void purgeMyStruct(MyStruct *); /* free internal members, doesn't free arg */

/* declare some other 'public' functions */

/* ... */

and the source

#include <stdlib.h>
#include "MyStruct.h"

typedef struct {
  int foo;
  /* etc, fields 'private' for the users implementing MyStruct */
} MyStructInternal;

MyStruct * createMyStruct()
{
   MyStruct * s = malloc(sizeof(MyStruct));

   initializeMyStruct(s);
   return s;
}

void initializeMyStruct(MyStruct * s)
{
   s->internal = malloc(sizeof(MyStructInternal));
   ((MyStructInternal *) s->internal)->foo = 1;
   /* initializing r->internal other fields */
}

void freeMyStruct(MyStruct * s)
{
    purgeMyStruct(s);
    free(s);
}

void purgeMyStruct(MyStruct * s)
{
  free(s->internal);
}

/* implement other 'public' functions participating on the implementation of MyStruct */

/* other 'private' functions participating on the hidden implementation of MyStruct */

You must offer both the creating and deleting functions because the real definition being hidden the user cannot allocate and free a MyStruct by himself.

Obviously a simpler/better way is to move to C++ because in a way you define a class ;-)

Sign up to request clarification or add additional context in comments.

5 Comments

We can also have struct internal; in the header file and struct internal {field defs}; in the source. Only restriction is that we can only use pointers to struct internal in our other files (or in our MyStruct in the header) as the type is incomplete there. Just saves us casting from void* in the source file.
for sure some variations exist, who say a struct is needed in the absolute and just a pointer to an int for instance is not enough ? ;-) I just indicate a way to hide the internal
BTW my code was wrong, you wake up me, must be ((MyStructInternal *) r->internal)->foo = 1;
This solution may have a problem. If MyStruct *hello = createMyStruct(); hello->internal = NULL;, then I can't free the MyStructInternal anymore within the freeMyStruct(MyStruct * s)?
by 'definition' internal must not modified by the user, however free(NULL)is perfectly legal and does nothing, but a memory leak will occurs and after the set to NULL the type will not be usable. All the solutions here are ersatz, the real solution is a C++ class :-)
5

I discovered this gem a few years ago. There's a way you can do struct inheritance in C.

But, the catch is, you'll just hide important data from the public. The data will still be there. If you plan, for example, to hide a dynamically allocated string, and the client attempts to free the pointer you provide on its own, it'll not free the hidden string. Yes, this can turn into memory leaks. Or worse, just reading garbage, if you typecast wrong children into the parent, then a function, which doesn't know anything about this context takes parent, and typecasts it into the expected type. It like, you're providing a Dog to a function that expects an Animal, and internally it converts to a Fish, because it expected a Fish, but it couldn't say so. It sounds crazy, but it leads to reading garbage, and to unexpected behaviors on the code.

So, use this at your own care.

One way I do it to prevent wrong typecasting (A->parent->B or Dog->Animal->Fish), is to use an enum variable within the parent struct, to tell the target functions about your intentions, as the c compiler will not complain about it. Also, one way of avoiding memory leaks is to make your own allocator/constructors and destructors, and ensure the client to use them. The client, in my pov, is anyone that is using the definitions listed in the header file.

main.c

#include <stdio.h>
#include <stdlib.h>
#include "hello.h"

// gcc -o app.exe main.c hello.c

int main(void) {
    Hello *hi;

    // This is what the public sees:
    printf("Size of hello: %u\n", sizeof(Hello));

    hi = hello_new();
    printf("World says %i\n", hi->world());
    hello_free(hi);

    return 0;
}

hello.h

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdint.h>

// This is what the public needs to see
typedef struct Hello {
    int (*world)(void);
} Hello;

Hello *hello_new();
void hello_free(Hello *h);

hello.c

#include "hello.h"

// This is what the source code can see.
typedef struct HelloInternal {
    Hello hello;
    int foo;
    int bar;
    int other;
    int fields;
    int many;
    int private;
    int only;
    int in_source_file;
} HelloInternal;

int hello_world() {
    return 42;
}

Hello *hello_new() {
    HelloInternal *hi = malloc(sizeof(HelloInternal));

    hi->hello.world = hello_world;

    hi->foo = 0;
    hi->bar = 1;
    hi->other = 2;
    hi->fields = 3;
    hi->many = 4;
    hi->private = 5;
    hi->only = 6;
    hi->in_source_file = 7;

    // Typecast to hide internal data
    return (Hello *) hi;
}

void hello_free(Hello *h) {
    free(h);
}

13 Comments

As an other answer the user has to know the apparent size is false, and then this is incompatible with array of Hello but only array of pointer to (I adapted my answer to add required functions to allow array element init/purge, but the struct definition was already compatible with array). Apart of that, inheritance is a bad wording for me ;-)
Yes, a single comment on the header file, or a good documentation is enough to inform the user. Nobody wants their app to crash, so they'll probably respect it.
you are very optimistic, for sure it is well known nobody make errors when programming ;-)
Well, yes. I lack xp of working with other people. I only do solo projects. Which why I state: "use this at your own care"
Also, I think it's better an array of Hello*, than an array of HelloInternal. The size in bytes is smaller as sizeof(Hello*) is commonly 8 bytes in 64-bit systems, and the size of HelloInternal is 40 bytes I think.
But yes, it's shady to use this technique. Its easy to make mistakes, and forces you to rely with dynamic memory allocation. Most of the time, it's just not the best solution. I, for example, prefer the most voted solution so far, yours. Explicitely stating that you'll hide data using a void ptr, is the best way to do it.
I think this solution is better. But I don't understand what this is incompatible with array of Hello but only array of pointer means? Do you mean sizeof(HelloInternal) != sizeof(Hello)?
yes sizeof(HelloInternal) != sizeof(Hello) then Hello arr[size]or a vector<Hello> are unusable, but Hello* arr[size]or a vector<Hello*> are possible. The goal of public/private is to hide in a secure way, to use without risk not knowing the private part, this is not the case with that solution for me ... and for the number of up votes , but of course you are free to prefer it anyway ;-)
I say that apart of the fact the proposal does not propose a way to have c-array/vector of Hello as I do, the problem is inherent with the used struct(s)
Well, just remember that this is a solution for C, not C++. Also, if you optmize padding in gcc, it won't work. In a more general case, you'll want to go through bruno's solution.
What do you mean for if you optmize padding in gcc, it won't work? Can you give some examples or explain what and why it is not working with some code?
its all explained on the link I provided in the first line of my answer. Just read that carefully.
you may also read The Lost Art of C Structure Packing if you're interested.
4

OP's code has two declarations of struct Hello in the same scope, which violates a constraint. Moreover, the two declarations have incompatible types.

bruno's answer shows one way to hide the private parts of an object, although it exposes a pointer to the private type in the public type. I will show a different method where the public type is embedded within the private type. This method is only suitable for situations where the private code is responsible for the allocation and freeing of individual objects of the public type for use with the public API.

Here is the original "hello.h" code with some additional declarations for allocating and freeing objects. The real header should also have "include guard" macros to guard against multiple declarations:

typedef struct Hello {
    int (*world) (void);
} Hello;

Hello *Hello_alloc(void);
void Hello_free(Hello *);

Now comes the example "hello.c" code:

#include <stdlib.h>
#include "hello.h"
#include "container_of.h"

struct Hello_internal {
    int foo;
    int bar;
    int other;
    int fields;
    int many;
    int private;
    int only;
    int in_source_file;

    Hello hello;
};

Hello *Hello_alloc(void)
{
    struct Hello_internal *hi;

    hi = calloc(1, sizeof *hi);
    if (hi == NULL) {
        return NULL;
    }
    hi->foo = 1;
    return &hi->hello;
}

void Hello_free(Hello *there)
{
    if (there) {
        struct Hello_internal *hi =
            container_of(there, struct Hello_internal, hello);
        free(hi);
    }
}

The code makes use of a container_of() macro to convert a pointer to a structure member into a pointer to the containing structure. Here is a simple version in "container_of.h" based on a more complicated version (that does type checking) in the Linux kernel source:

#ifndef CONTAINER_OF_H__INCLUDED
#define CONTAINER_OF_H__INCLUDED

#include <stddef.h>

#define container_of(ptr, type, member) \
    ((type *)((char *)(ptr) - offsetof(type, member)))

#endif

The argument for the member parameter can be any member designator allowed by the second argument of the standard offsetof() macro. offsetof(type, member) calculates the offset in bytes of designated member member from the beginning of an object of type type. The container_of() macro subtracts that offset in bytes from the given ptr (that is assumed to point to the designated member of an object of containing type type), resulting in a pointer to the containing object of type type.

2 Comments

As an other answer the user has to know the apparent size is false, and then this is incompatible with array of Hello but only array of pointer to (I adapted my answer to add required functions to allow array element init/purge, but the struct definition was already compatible with array)
True. I forgot to mention that as a limitation. I have done so now.
3

But they are still unrelated.

typedef struct {
    int (*world) (void);
} Hello;



struct Hello {
    int foo;
    int bar;
    int other;
    int fields;
    int many;
    int private;
    int only;
    int in_source_file;
    
    Hello hello;
};


int xxx(void)
{
    return 2;
}


int main(void)
{
    struct Hello* hello = malloc(sizeof(*hello));
    hello -> foo = 1;
    hello -> hello.world = xxx;
}

1 Comment

I updated the post and added the #include "hello.h" in the .c file, please check my updated post.
3

You can hide the implementation as long as you make sure the public definition has the same size as the private one and the public fields have the same position.

Here is a simplistic example:

  • public header file
#ifndef Hello_private
#define Hello_private  int private[8]
#endif

struct Hello {
    Hello_private;
    
    int (*world)(void);
};
  • private header file:
#define Hello_private    \
    int foo;             \
    int bar;             \
    int other;           \
    int fields;          \
    int many;            \
    int private;         \
    int only;            \
    int in_source_file

#include "hello.h"

Hello instances can be declared or allocated from user code as long as they get initialized by private code or if zero initialization is performed and sufficient.

You can ensure consistency by adding static_assert statements about the size of the Hello structure in both public and private headers.

Also note that alignment of the private fields and their public placeholder must be carefully chosen so both structures have the same alignment requirement.

This solution is brittle as the code must be adjusted and recompiled if the implementation changes, so the private handling should not be compiled in a dynamic library where consistency can no longer be tested between user code and the implementation.

5 Comments

To move Hello_private at the end of the definition removes without cost the wrong alignment problem between the 'public' and 'private' definitions (the compiler is not allowed to reorder fields), even if of course the same size for both is still needed (but as you say verified by a static_assert).
This is an interesting alternative to my proposal, deserving an UV ;-)
I'd only resort to something like this if it was necessary to declare an object of type struct Hello in the public code.
as for mine, the advantage of chqrlie's proposal is to be compatible with arrays because the size is the right size
True, but the private code of chqrlie's proposal needs to break the strict aliasing rule to access objects declared by the public code (unless using memcpy or whatever, or all the member types are identical at the bottom level).
2

One option is to document which members of the type are public. Code using the API should only use the public members. The private members could follow some naming convention to mark them as private. For example, in the public header "hello.h":

typedef struct Hello {
    int _struct_Hello__private__foo;
    int _struct_Hello__private__bar;
    int _struct_Hello__private__other;
    int _struct_Hello__private__fields;
    int _struct_Hello__private__many;
    int _struct_Hello__private__private;
    int _struct_Hello__private__only;
    int _struct_Hello__private__in_source_file;

    int (*world) (void);
} Hello;

void Hello_init(Hello *);
void Hello_uninit(Hello *);
Hello *Hello_alloc(void);
void Hello_free(Hello *);

The implementation code in "hello.c" can use a helper macro to save typing the private member names in full:

#include "hello.h"
#include <string.h>
#include <stdlib.h>

#define PM(name) _struct_Hello__private__##name

void Hello_init(Hello *hello)
{
    memset(hello, 0, sizeof *hello);
    hello->PM(foo) = 1;
    hello->PM(bar) = 42;
}

void Hello_uninit(Hello *hello)
{
}

Hello *Hello_alloc(void)
{
    Hello *hello = malloc(sizeof *hello);

    if (hello) {
        Hello_init(hello);
    }
    return hello;
}

void Hello_free(Hello *hello)
{
    if (hello) {
        Hello_uninit(hello);
        free(hello);
    }
}

A similar macro could be defined in "hello.h" temporarily to save typing when defining the type:

typedef struct Hello {
#define S_H_P(name) _struct_Hello__private__##name
    int S_H_P(foo);
    int S_H_P(bar);
    int S_H_P(other);
    int S_H_P(fields);
    int S_H_P(many);
    int S_H_P(private);
    int S_H_P(only);
    int S_H_P(in_source_file);
#undef S_H_P
    int (*world) (void);
} Hello;

Comments

1

Let's differentiate between declaration and definition. A declaration is only declaring something, like a function which can be implemented (defined). In your header file you define a type and in your c file you define another type of the same name. If you want to have further fields, then you could have them in the header and initialize the value (but not the type definition) in your C file.

1 Comment

I forgot to add the #include, now added, please check my updated post.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.