Lars Schneider
Lars Schneider

Reputation: 5692

Is it possible to exchange a C function implementation at run time?

I have implemented a facade pattern that uses C functions underneath and I would like to test it properly.

I do not really have control over these C functions. They are implemented in a header. Right now I #ifdef to use the real headers in production and my mock headers in tests. Is there a way in C to exchange the C functions at runtime by overwriting the C function address or something? I would like to get rid of the #ifdef in my code.

Upvotes: 1

Views: 223

Answers (4)

JamesR
JamesR

Reputation: 745

It is possible With Typemock Isolator++ without creating unnecessary new levels of indirection. It can be done inside the test without altering your production code. Consider the following example:

You have the Sum function in your code:

int Sum(int a, int b)
{
    return a+b;
}

And you want to replace it with Sigma for your test:

int Sigma(int a, int b)
{
    int sum = 0;

    for( ; 0<a ; a--)
    {
        sum += b;
    }

    return sum;
}

In your test, mock Sum before using it:

WHEN_CALLED: call the method you want to fake.

ANY_VAL: specify the args values for which the mock will apply. in this case any 2 integers.

*DoStaticOrGlobalInstead: The alternative behavior you want for Sum. In this example we call Sigma instead.

TEST_CLASS(C_Function_Tests)
{
public:

TEST_METHOD(Exchange_a_C_function_implementation_at_run_time_is_Possible)
{
    void* context = NULL; //since Sum global it has no context
    WHEN_CALLED(Sum (ANY_VAL(int), ANY_VAL(int))).DoStaticOrGlobalInstead(Sigma, context);

    Assert::AreEqual(2, Sum(1,2));
}
};

*DoStaticOrGlobalInstead It is possible to set other types of behaviors instead of calling an alternative method. You can throw an exception, return a value, ignore the method etc...

For instance:

TEST_METHOD(Alter_C_Function_Return_Value)
{
    WHEN_CALLED(Sum (ANY_VAL(int), ANY_VAL(int))).Return(10);
    Assert::AreEqual(10, Sum(1,2));
}

Upvotes: 1

Theodoros Chatzigiannakis
Theodoros Chatzigiannakis

Reputation: 29223

I don't think it's a good idea to overwrite functions at runtime. For one thing, the executable segment may be set as read-only and even if it wasn't you could end up stepping on another function's code if your assembly is too large.

I think you should create something like a function pointer collection for the one and the other set of implementations you want to use. Every time you want to call a function, you'll be calling from the selected function pointer collection. Having done that, you may also have proxy functions (that simply call from the selected set) to hide the function pointer syntax.

Upvotes: 0

Bernd Elkemann
Bernd Elkemann

Reputation: 23560

It is strange that you even need an ifdef-selected header. The code-to-test and your mocks should have the exact same function signatures in order to be a correct mock of the module-to-test. The only thing that then changes between a production-compilation and a test-compilation would be which .o files you give to the linker.

Upvotes: 1

enhzflep
enhzflep

Reputation: 13109

To expand on Bart's answer, consider the following trivial example.

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

int (*functionPtr)(const char *format, ...);

int myPrintf(const char *fmt, ...)
{
    char *tmpFmt = strdup(fmt);
    int i;
    for (i=0; i<strlen(tmpFmt); i++)
        tmpFmt[i] = toupper(tmpFmt[i]);

// notice - we only print an upper case version of the format
// we totally disregard all but the first parameter to the function
    printf(tmpFmt);

    free(tmpFmt);
}

int main()
{

    functionPtr = printf;
    functionPtr("Hello world! - %d\n", 2013);

    functionPtr = myPrintf;
    functionPtr("Hello world! - %d\n", 2013);

    return 0;
}

Output

Hello World! - 2013
HELLO WORLD! - %D

Upvotes: 1

Related Questions