What's the effect of empty macro `GRPCAPI` before gRPC function declaration?

109 Views Asked by At

When I read the code in gRPC, I found many APIs using GRPCAPI as qualifier.

GRPCAPI grpc_channel* grpc_cronet_secure_channel_create(
    void* engine, const char* target, const grpc_channel_args* args,
    void* reserved);

When I click the link to GRPCAPI, it's an empty macro.

#ifndef GPRAPI
#define GPRAPI
#endif

#ifndef GRPCAPI
#define GRPCAPI GPRAPI
#endif

I understand some usages of empty macro:

  • preventing multiple copies of the same header being included
  • used as a switch for debug or removing sensitive code

But here the GRPCAPI belongs to neither. Is it just a marker to tell us the function is an API? Or more effect for document or others functions?

2

There are 2 best solutions below

0
On BEST ANSWER

I really appreciate the answer from @Raildex and comment from @paulsm4, which are very inspiring.

But the exactly function of GRPCAPI and GPRAPI is used to mark APIs as a label.

There is a script in grpc named list_api.py using the label GPRAPI and GRPCAPI, which is the only place using these two labels.

_RE_API = r'(?:GPRAPI|GRPCAPI|CENSUSAPI)([^;]*);'

for m in re.finditer(_RE_API, text):   # match file content
...

After running the script, we'll get:

...
- arguments: void* engine, const char* target, const grpc_channel_args* args, void*
    reserved
  header: include/grpc/grpc_cronet.h
  name: grpc_cronet_secure_channel_create
  return_type: grpc_channel*
...
3
On

Its either for platform specific attributes or for future attributes.

For Windows DLLs, you usually specify
__declspec(dllexport) when compiling the library and
__declspec(dllimport) when consuming the library.

A macro is the convenient way to both compile and consume it because you only define the value of the macro to be __declspec(dllexport)/__declspec(dllimport) within the header.
the same goes for other compiler attributes, such as __attribute__(visibility(default))/__attribute__(visibility(hidden)) on GCC

Now when the library is statically linked, you don't need all that and you define the macro to have no value.
An example would be:

#ifdef STATIC_LIBRARY
     #define LIBRARY_API 
#else
#ifdef LIBRARY_EXPORTS
#    define LIBRARY_API __declspec(dllexport)
#else
#    define LIBRARY_API __declspec(dllimport)
#endif


LIBRARY_API void foo(); // when statically linked, it's a simple void function. If dynamically linked, it's either dllexport when compiling the library or dllimport when consuming it.

Another explanation can be a calling convention modifier. x86 (I only know x86) has different calling conventions - these decide how the caller/callee treats parameters of functions on the assembly level.

Something like this:

#if WIN32
#define APICALL cdecl
#else
#define APICALL 
#endif

APICALL void foo() // uses cdecl on WIN32