views:

42

answers:

1

OK, I have sort of a tricky problem I'm attempting to solve. First of all, an overview:

I have an external API not under my control, which is used by a massive amount of legacy code.

  • There are several classes of bugs in the legacy code that could potentially be detected at run-time, if only the external API was written to track its own usage, but it is not.
  • I need to find a solution that would allow me to redirect calls to the external API into a tracking framework that would track api usage and log errors.
  • Ideally, I would like the log to reflect the file and line number of the API call that triggered the error, if possible.

Here is an example of a class of errors that I would like to track. The API we use has two functions. I'll call them GetAmount, and SetAmount. They look something like this:

// Get an indexed amount
long GetAmount(short Idx);

// Set an indexed amount
void SetAmount(short Idx, long amount);

These are regular C functions. One bug I am trying to detect at runtime is when GetAmount is called with an Idx that hasn't already been set with SetAmount.

Now, all of the API calls are contained within a namespace (call it api_ns), however they weren't always in the past. So, of course the legacy code just threw a "using namespace api_ns;" in their stdafx.h file and called it good.

My first attempt was to use the preprocessor to redirect API calls to my own tracking framework. It looked something like this:

// in FormTrackingFramework.h
class FormTrackingFramework
{
    private:
        static FormTrackingFramework* current;

    public:
        static FormTrackingFramework* GetCurrent();

        long GetAmount(short Idx, const std::string& file, size_t line)
        {
            // track usage, log errors as needed
            api_ns::GetAmount(Idx);
        }
};

#define GetAmount(Idx) (FormTrackingFramework::GetCurrent()->GetAmount(Idx, __FILE__, __LINE__))

Then, in stdafx.h:

// in stdafx.h

#include "theAPI.h"

#include "FormTrackingFramework.h"

#include "LegacyPCHIncludes.h"

Now, this works fine for GetAmount and SetAmount, but there's a problem. The API also has a SetString(short Idx, const char* str). At some point, our legacy code added an overload: SetString(short Idx, const std::string& str) for convenience. The problem is, the preprocessor doesn't know or care whether you are calling SetString or defining a SetString overload. It just sees "SetString" and replaces it with the macro definition. Which of course doesn't compile when defining a new SetString overload.

I could potentially reorder the #includes in stdafx.h to include FormTrackingFramework.h after LegacyPCHIncludes.h, however that would mean that none of the code in the LegacyPCHIncludes.h include tree would be tracked.

So I guess I have two questions at this point: 1: how do I solve the API overload problem? 2: Is there some other method of doing what I want to do that works better?

Note: I am using Visual Studio 2008 w/SP1.

A: 

Well, for the cases you need overloads, you could use a class instance that overloads operater() for a number of parameters.

#define GetAmount GetAmountFunctor(FormTrackingFramework::GetCurrent(), __FILE__, __LINE__)

then, make a GetAmountFunctor:

 class GetAmountFunctor
 {
    public:
      GetAmountFunctor(....) // capture relevant debug info for logging
      {}

      void operator() (short idx, std::string str) 
      {
           // logging here
           api_ns::GetAmount(idx, str);
      }

      void operator() (short idx) 
      {
           /// logging here
           api_ns::GetAmount(Idx);
      }
 };

This is very much pseudocode but I think you get the idea. Whereever in your legacy code the particular function name is mentioned, it is replaced by a functor object, and the function is actually called on the functor. Do consider you only need to do this for functions where overloads are a problem. To reduce the amount of glue code, you can create a single struct for the parameters __FILE__, __LINE__, and pass it into the constructor as one argument.

jdv
I am surprised this is getting no response... Is there anything unclear I need to explain?
jdv
This method is far more elegant than the one I went with (http://www.codeproject.com/KB/tips/va_pass.aspx). Thank you - I will play around with this method. I'm assuming you could use this with a variable argument function, in the style of printf, with the appropriate operator() defined in the functor?
Jeremy Bell
looks like I still need the stack gobbler from the codeproject page to pass variable arguments from one function to another. But, I can still use this for the overloads. Thanks again.
Jeremy Bell