mbeddr / mbeddr.core

The mbeddr core. An extensible C
Eclipse Public License 2.0
224 stars 77 forks source link

export a function prototype #1322

Open wuryel opened 8 years ago

wuryel commented 8 years ago

Sometimes it may be necessary to export a function prototype. Thus having it declared in the header file. This may be necessary if such a function is refered to in an (included) header file. (In my case a linked library is configured in a header file by setting #defines to specific values. One of these values is the address of a function.)

A simple fix is suggested in #1316 -> with this modification the prototype is declared twice in the header and once in the c file -> not really supported by the editor (only via Inspector)

Possibly a bug?: Why can't be a FunctionPrototype be a child of an IModule? It can be inside a section inside an IModule !

coolya commented 8 years ago

I'm not quite sure what the use case is here. In general function prototypes should not be part of ImplementationModule so it is probably a bug that you can put them there if they are in a section.

It looks like you want to expose some sort of API to external users via a header file? You can put the API you want to expose in a ExternalModule and turn on code generation for that module via the "Toggle Generate Header" intention. This will generate a none mangled H file. If you than want to implement that declared function prototype you can use the "implements annotation":

http://mbeddr.com/2015/05/15/implements-anotation.html

This way you can have a unified header file exposing all your API while the (default) implementation structure does not need to match that.

Does this solve the use case you have?

wuryel commented 8 years ago

In my case I have an application that needs a library. (The library is the adoption to the OS engaged) Depending on the library implementation engaged different datatype definitions are necessary.

So the public API for example has a function void foo(struct mystructure* arg);

now I have two library implementations providing the function but different definitions of the structure, e.g. in library implementation A: struct mystructure { uint32_t a; }; and in library implementation B: struct mystructure { uint32_t a; uint32_t b; uint32_t c; uint32_t d;};

In the application (that uses either A or B) I do not need and want to access the internals of the structures.

When generating the header from the External Module (as you suggested) this doesn't work as I would have to decide for a specific structure definition. The workaround is to completely decouple the external module that is refered to by the implementation and the libraries implementations. The external file imports the header file of the selected library implementation. Of course they have to be named identical and all function names must be matching.

An alternative would be to use void* pointers in the API that are casted accordingly in the implementation ... but casting void* (although MISRA compliant) is somewhat critical that I would like to avoid - at least in the manually written code.

In essence I would need some kind of virtual structure definition in the external module which has a concrete definition in the library implementations.

Do you understand what I mean? What do you think, have I overseen some solution?

coolya commented 8 years ago

To verify that I understand correctly what you do I have created a little sample in plain C.

common.h:

#pragma once
struct foo;

struct foo* my_init();
int my_function(struct foo*); 

implA/impl.c:

#include "../common.h"
#include <stdlib.h>

struct foo
{
    int member;
    int second;
};

struct foo* my_init() {
    void* data = malloc(sizeof (struct foo));
    /*do stuff here */
    return data;
}
int my_function(struct foo* data) {
    return 1;
}

implB/impl.c:

#include "../common.h"
#include <stdlib.h>
struct foo
{
    int member;
};

struct foo* my_init() {
    void* data = malloc(sizeof (struct foo));
    /*do other stuff here */
    return data;
}
int my_function(struct foo* data) {
    return 0;
}

main.c:

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

int main(int argc, char const *argv[])
{
    struct foo* myData = my_init();

    int ret = my_function(myData);
    if(ret)
        printf("implA\n");
    else
        printf("implB\n");
    /* code */
    return 0;
}

In order to compile against Implementation A you would do:

gcc main.c implA/implA.c -o test

and for Implementation B:

gcc main.c implB/implB.c -o test

is this correct?

wuryel commented 8 years ago

yes, exactly.

coolya commented 8 years ago

I got your point. This is definitely a limitation of mbeddr and we should find a way to express this.

I best case I would like to find a solution that allows us to make this relationship explicit. I see some benefits from not using a void* here:

How about a way to declare a abstract (we might need a better name for it?) struct with no implementation that can be used as a usual type. And then other structs can make that struct "concrete". This way we can asserts each compilation unit only contains one concrete struct of that.

What do you think?

wuryel commented 8 years ago

yes, if I got it right, this would be: (maybe the term structure prototype fits our needs?)

Transformation to C would be straight forward following your sample code.