Quantcast
Channel: Embarcadero Community - Embarcadero Community
Viewing all articles
Browse latest Browse all 3212

Compiling HiRedis, the official C Client of the Windows port of Redis with C++ Builder

$
0
0

Hi all,

I'm in the process of building Hiredis, the official C client library for Redis (Windows port of course) using C++ Builder.

Windows port of Redis is a fork maintained by Microsoft Open Tech community and is of course tight to MS compiler and libraries. In fact, there is no luck in trying to compile the source just "out of the box" as Visual Studio does, even if the source is straight standard C (well, at last the original Redis code).

The source can be found here: Windows port of Redis 

I know there is a Delphi client available, but it really sounds sad (and frustrating) to me having to import the Delphi code because the "native C source" cannot be used. No way. That's why I decided to give it a try. 

To compare sources an libraries involved, I'm using C++ Builder 10.1.2 (Berlin Update 2) on one side and Visual Studio Community 2017 on the other side. The basic steps using C++ Builder are:

(Edit: I edited the post and removed all code formatting features because I had problems with it. Also, for the same reason all #include in the post has standard parenthesis instead of "minor and major" chars. Sorry for that.)

  1. Download of source version redis-win-3.2.100
  2. Create a new Static Library project called Hiredis and add some files contained in the "redis-win-3.2.100\deps\hiredis" subfolder:
    async.c
    hiredis.c
    net.c
    sds.c

    There is a solution project available for Visual Studio under "redis-win-3.2.100\msvs\hiredis" ready to compile, that's where I took the list of files to add. Headers are also added to the MS project, but as far as I can see, there is no difference if you keep them off.
    Anyway headers are:
    async.h
    fmacros.h
    hiredis.h
    net.h
    sds.h
    win32_types.h

  3. Setup the project to use Clang based compiler (not really needed as this should be standard C code, but see later)
  4. Build

The project fails to compile as I said, here is a step by step description of the issues and fixes I used. After each fix I usually compile the single unit or directly build the whole project. Each fix provided must not break the original source which still compiles using MSVC, this will be eventually a requirement when talking about pushing the code to the MSOpen Tech community.

  • First error: [bcc32c Error] Win32_Error.h(37): unknown type name 'size_t'
    Fix: added a conditional block and the relative include to the header that contains that type. 

    #ifdef __BORLANDC__
    #include (stdlib.h)
    #endif

    This error is due to the fact that inside Win32_Error.h there is another include and this header in MS C Runtime Library version contains another include to a Visual C header. (Kudos to Embarcadero C Runtime Library version)

  • Second error: 
    [bcc32c Error] _stddef.h(45): typedef redefinition with different types ('int' vs 'long') 
    Win32_types_hiredis.h(44): previous definition is here

    Fix: added conditional block wrapper to avoid type re declaration. 

    #ifndef __BORLANDC__
    typedef long              ssize_t;
    #endif

    _stddef.h already contains that type (int instead of long, but it's quite good for now)

  • Third error: [bcc32c Error] stdio.h(56): typedef redefinition with different types ('long' vs 'long long')
    This error is due to the re-definition of the type off_t inside the file win32_types.h provided in the hiredis source.

    Now, according to the info in "win32_types.h" they need to re-define both off_t and _off_t types to match Posix version of Redis.
    But, in order to use this definition without conflicts you need to include this header before any other inclusion of the original header "sys\types.h", which contains the original types definitions under a conditional define (_OFF_T_DEFINED). 

    Win32_types.h will also define _OFF_T_DEFINED to avoid subsequent types re-definitions. (anyway MSVC has an additional conditional define _OFF_T_DEFINED at project level.)

    Now, on MS Visual Studio C Runtime Library, "stdio.h" does not contain "off_t" type definition, that's why it everything works. Moreover, Embarcadero C Runtime Library has no conditional defs to skip off_t and _off_t definitions inside "sys\types.h" as MS does.

    First Fix: I'd rather not modify C Runtime Library headers at all, but at the moment I have no other options. To minimize modifications on such files, I choose to "cut off" stdio.h definitions by adding a define just above the header inclusion, inside the file "Win32_FDAPI.h":

    #ifdef __BORLANDC__
    #define __STDC__ 1
    #endif
    #include (stdio.h)

    Luckily (..well, hopefully) _off_t is wrapped in a conditional #if !defined(__STDC__), that's why the definition just above the include does the job. Unfortunately this solution cuts away some other options (#pragma warn -nak for example) and others, but it seems to be OK, for now anyway.

    Second Fix: I have no other option here than modifying Embarcadero C Runtime Library header file "sys\types.h" and wrap the off_t type definition with the above mentioned _OFF_T_DEFINED, just as in MS Visual Studion C Runtime headers:

    #ifndef _OFF_T_DEFINED
    #define _OFF_T_DEFINED
    typedef long off_t;
    #endif

     

  • Fourth error: [CLANG Error] sds.h(53): expected identifier
    The file contains an helper macro to pack structs. The macro uses __pragma() compiler extension to allow the usage of pragma "in-a-macro". According to the compiler documentation, also thanks to this answer on StackOverflow from Remy Lebeau, old BCC32 does not support this statements. But Clang based compilers do. This is the first fix that is tight to Clang compiler, even if we could build a different solution by using #pragma as stated in stakoverflow post by Remy to have BCC32 working.
    Anyway, I was not able to make __pragma() work that way, so I created another macro using _Pragma() instead and that worked fine.

    #if defined(__BORLANDC__) && defined(__clang__)
    #define PACK( __Declaration__ ) _Pragma( "pack(push, 1)" ) _Declaration__ _Pragma( "pack(pop)" )
    #else
    #define PACK( __Declaration__ ) __pragma( pack(push, 1) ) __Declaration__ __pragma( pack(pop) )
    #endif

     

  • Fifth and sixth errors: 
    [CLANG Error] win32_types.h(37): typedef redefinition with different types ('long long' vs 'long') 
    stdio.h(56): previous definition is here
    First Fix: same solution used for "third error". Add __STDC__ definition just above the include inside "net.c"
    Second Fix: according to the remark near stdio.h include, that header is included only to provide "size_t" type. Actually on Embarcadero C Runtime Library, that type is defined in "stdlib.h".
    Therefore I added a conditional block to include "stdlib.h" instead of "stdio.h" inside "hiredis.h" source.

    #ifdef __BORLANDC__
    #include (stdlib.h) /* for size_t */
    #else
    #include (stdio.h) /* for size_t */
    #endif

     

  • Seventh error. Here is where I stuck.
    [CLANG Error] hiredis.c(1041): conflicting types for 'redisConnectWithTimeout'
    hiredis.h(184): previous declaration is here

    As sometime happens the compiler message is not really helpful. In fact the problem is not 'redisConnectWithTimeout' function prototype but rather one of the parameter types used in it, timeval which is defined in "winsock2.h".
    For some strange reason, on MSVC this type definition is known in hiredis.h at function declaration, even if winsock2.h is not included by any of the include entries, nor in any sub-includes. Unfortunately, any inclusion of winsock2.h leads to an incredible amount of other errors which make me think about searching for another solution.

Here is where I lay by now, welcome contributions from anyone can help.

Thanks.

Alex B.




Viewing all articles
Browse latest Browse all 3212

Trending Articles