cvisual-studiolinkercmocka

visual studio c linker wrap option?


From this article Unit testing with mock objects in C:

This is done by using the --wrap linker option which takes the name of the wrapped function as an argument. If the test was compiled using gcc, the invocation might look like:

$ gcc -g -Wl,--wrap=chef_cook waiter_test.c chef.c

How can I do this when compiling a C project in visual studio?


Solution

  • The --wrap in ld can be emulated by the /ALTERNATENAME option in MSVC Linker.

    We start from two compilation units, say foo.o compiled from foo.c, whose external functions are declared in foo.h, and main.o from main.c. (If foo has been compiled as a library, things won't change much.)

    // foo.h
    int foo();
    
    // foo.c
    int foo() {
        return 0;
    }
    
    // main.c
    #include <stdio.h>
    #include "foo.h"
    int main() {
        int x = foo();
        printf("%s\n", x ? "wrapped" : "original");
    }
    

    The return value of int foo() is 0, so the snippet of code above will output "original".

    Now we override the actual implementation by an alias: The #include "foo.h" in main.c is replaced by

    #define foo real_foo
    #include "foo.h"
    #undef foo
    #pragma comment(linker, "/alternatename:real_foo=foo")
    

    Let me explain what happens here:

    1. by #define foo real_foo, the function declaration in foo.h is modified as int real_foo().
    2. However, the symbol in foo.o is still named after int foo(), instead of the alias int real_foo(). That's why we need the /alternatename linker switch.
    3. "/alternatename:real_foo=foo" tells the linker that, if you cannot find the symbol called real_foo, try foo again before throwing an error.
    4. Apparently there is no definition of int real_foo(). MSVC Linker will search for int foo() and link it instead at each occurrence of int real_foo().

    As the previous implementation has been aliased, now we redirect int foo() to our new implementation by a macro:

    int wrap_foo() {
        return real_foo() + 1;
    }
    #define foo wrap_foo
    

    And we are done here. At last the main.cpp looks like:

    #include <stdio.h>
    
    #define foo real_foo
    #include "foo.h"
    #undef foo
    #pragma comment(linker, "/alternatename:real_foo=foo")
    
    int wrap_foo() {
        return real_foo() + 1;
    }
    #define foo wrap_foo
    
    int main() {
        int x = foo();
        printf("%s\n", x ? "wrapped" : "original");
    }
    

    Built in MSVC, it will output "wrapped".