dhu
dhu

Reputation: 718

How to add new argument to existing code in C?

I was working an existing piece of C++ code that calls underlying C functions. The current code already works fine but what I have to do is to pass in a few new arguments and use these new arguments to modify some behaviors. The idea is demonstrated by the code below:

   // this is how the existing works look like without my changes:
   do_something_case1(int p1, double p2, struct1* p3, ...)
   {
       ... // do a lot of things

       // my work is to give client ability to override config1, config2
       // with their own structures. 
       // get_config1() and get_config2() are existing pre-defined 
       // configurations and are used extensively elsewhere. 

       ConfigStruct1 config1 = get_config1(...);
       ... // do a lot of things
       ConfigStruct2 config2 = get_config2(...);
       ... // do something
       foo(config1, config2, ...);
       ... // more code
   }

   do_something_case2(int p1, double p2, struct1* p3, ...)
   {
       // in a similar situation as do_something_case1
   }

   ... // more do_something_case3, do_something_case4 defined here

   // this function needs to take client overrides. 
   void do_something(int p1, double p2, struct1* p3, ...)
   {
       if(case1) 
       {
           do_something_case1(p1, p2, p3, ...);
       }
       else if(case2) 
       {
           do_something_case2(p1, p2, p3, ...);
       }
       else if(case3)
       {
           do_something_case3(p1, ...);
       }
       else if(...) // more if statements to come
       {
           ... // do something
       }
   }

My work is to give client ability to use their own ConfigStruct. In principal ConfigStruct1, ConfigStruct2 should be inputs instead of being decided in the function which don't give client much freedom to override them. However these code are very old and are used extensively in the system and I don't want to break any existing client code. And I definitely want to take advantage of all the rest complex logic that's already defined in these do_something_case() functions. If this were C++, I could simply add a new argument that takes a default value,

    // if it were C++, I could do:
    do_something_case1(int p1, double p2, struct1* p3, ..., 
                       ConfigStruct1* pConfig1=NULL, 
                       ConfigStruct2* pConfig2=NULL)
    {
        if(NULL == pConfig1)
        {
            // call get_config1(..)
        }
    }

or use function overloading. But since C doesn't support either, I feel that I am stuck with fewer and uglier options. One way that solves the problem is to copy and paste all these functions and make surgical changes in the new ones. That will make it even uglier. Do you have any suggestions to help my situation?

Thank you very much.

Upvotes: 0

Views: 370

Answers (1)

John Bollinger
John Bollinger

Reputation: 180181

If this were C++, I could simply add a new argument that takes a default value

Only if you were willing and able to recompile everything that uses this function. It sounds like that might be a major hassle, and perhaps not even viable. There is a lot more smoke & mirrors in C++ than there is actual magic.

[...] or use function overloading.

C++ overloaded functions are bound early -- or at least, the specific overloaded signature used in any particular call is selected at compile time. Therefore, simply adding an overloaded version would not be sufficient to make existing code ever call the new version. Instead, again, you would need to recompile everything. Smoke, mirrors, no magic.

For a solution in C, changing the number of arguments to the do_something_case() functions is pretty much a non-starter. Not only would that require you to recompile everything, it would require you to modify every call. You could probably leverage the C preprocessor to automate some of that, but it's still bound to be messy.

It looks like the get_config1() and get_config2() functions might be viable extension points, however, supposing that these return pointers rather than actual structures as depicted in the question. This presumes that the client structures will conform to the expected config structure types, with additions at the end, which seems like it would anyway be required for the functions called by the do_something_case()s to work. You can possibly modify the get_config() functions however works best, or perhaps set up a mechanism by which the client can provide its own implementations of them via strategic use of the dynamic linker.

Upvotes: 1

Related Questions