penguin4hire
penguin4hire

Reputation: 325

How to enforce an API definition with macros or some other mechanism?

I have a C header file as part of an API that I am developing which exposes a number of functions. For example:

api.h

void foo();
int bar(char * foobar);

Now, most functions that I expose are actually defined within the API, however, there are some that I leave to the using-developer to implement herself. To make it easier for the developer and enforce conformance to my API I added the following macros:

api.h(modified)

#define DEFINE_FOO() \
        void foo()
void foo();
#define DEFINE_BAR() \
        int bar(char * foobar)
int bar(char * foobar);

to be used as follows:

implement.c

#include "api.h"
DEFINE_FOO() {
    // Codez
}

DEFINE_BAR() {
    // More codez
}

One of the things that bothers me about this approach is that the developer will use the DEFINE_* macros but not necessarily intuit that a function, namely "bar" takes arguments. Is there a way to improve this approach or is there a better approach to enforcing custom API definitions in C? Thanks!

Upvotes: 0

Views: 768

Answers (2)

Craig Estey
Craig Estey

Reputation: 33601

There's really no need to use macros and I'd recommend against it (and I've done plenty of "crazy" things with macros over the [last 35+] years).

If you're defining the API, this implies that you're putting [ANSI] prototypes in api.h for all your functions. That doesn't mean you will implement them--it only means that you define the architecture/API. Any function that doesn't have a prototype in api.h isn't part of the API [obviously].

If you do that, the compiler will flag any mismatches. Simply require that the developer include the api.h at the top.

Here's an api.h:

void foo(void);
int bar(char *foobar);

A well conforming .c will compile cleanly:

#include "api.h"
#include <stdio.h>

void
foo(void)
{
}

int
bar(char *str)
{
    return 0;
}

A non-conformant .c will not compile correctly:

#include "api.h"
#include <stdio.h>

void
foo(int x)
{
    printf("foo: %d\n",x);
}

int
bar(char *str)
{
    return 0;
}

You'll get compilation errors:

bad.c:5:1: error: conflicting types for ‘foo’
 foo(int x)
 ^
In file included from bad.c:1:0:
api.h:2:6: note: previous declaration of ‘foo’ was here
 void foo(void);
      ^

So, if a developer makes a mistake, the ordinary code definitions will handle it.

Upvotes: 3

user2525056
user2525056

Reputation: 83

I wouldn't really recommend your approach, but you can technically make it work.

You can pass arguments into macros as such:

#define DEFINE_BAR(arg) \
        int bar(char* arg)

Now the programmer can call

DEFINE_BAR(arg_name) {
    return strlen(arg_name);
}

This will be turned into the following by cpp:

int bar(char* arg_name) {
    return strlen(arg_name);
}

Upvotes: 2

Related Questions