对于外部API的运行时跟踪,是否有更好的替代预处理器重定向?

时间:2010-02-22 19:59:45

标签: c++ redirect c-preprocessor bug-tracking

我有一个棘手的问题,我试图解决。首先,概述:

我有一个不受我控制的外部API,大量遗留代码会使用它。

  • 遗留代码中有几类错误可能会在运行时被检测到,如果只编写外部API来跟踪自己的用法,但事实并非如此。
  • 我需要找到一个解决方案,允许我将对外部API的调用重定向到跟踪api使用和记录错误的跟踪框架。
  • 理想情况下,如果可能,我希望日志能够反映触发错误的API调用的文件和行号。

以下是我想要跟踪的一类错误的示例。我们使用的API有两个功能。我将它们称为GetAmount和SetAmount。他们看起来像这样:

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

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

这些是常规 C 功能。我试图在运行时检测到的一个错误是使用尚未使用SetAmount设置的Idx调用GetAmount。

现在,所有API调用都包含在命名空间中(称之为api_ns),但它们并不总是在过去。所以,当然遗留代码只是抛出了“using namespace api_ns;”在他们的stdafx.h文件中并称其为好。

我的第一次尝试是使用预处理器将API调用重定向到我自己的跟踪框架。它看起来像这样:

// 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__))

然后,在stdafx.h中:

// in stdafx.h

#include "theAPI.h"

#include "FormTrackingFramework.h"

#include "LegacyPCHIncludes.h"

现在,这适用于GetAmount和SetAmount,但是存在问题。 API还有一个SetString(短Idx,const char * str)。在某些时候,为了方便起见,我们的遗留代码添加了一个重载:SetString(短Idx,const std :: string& str)。问题是,预处理器不知道或不关心您是在调用SetString还是定义SetString重载。它只是看到“SetString”并将其替换为宏定义。当定义新的SetString重载时,当然无法编译。

我可能会在stdafx.h中对#includes重新排序,以便在LegacyPCHIncludes.h之后包含FormTrackingFramework.h,但这意味着将跟踪LegacyPCHIncludes.h包含树中的所有代码。

所以我想我现在有两个问题: 1:如何解决API过载问题? 2:还有其他方法可以做我想做的更好的方法吗?

注意:我使用的是Visual Studio 2008 w / SP1。

2 个答案:

答案 0 :(得分:1)

那么,对于需要重载的情况,可以使用一个为operater()重载多个参数的类实例。

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

然后,制作一个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);
      }
 };

这是非常伪代码,但我认为你明白了。在遗留代码中,如果提到特定的函数名称,它将被仿函数对象替换,并且该函数实际上是在仿函数上调用的。请注意,您只需要对过载有问题的函数执行此操作。要减少粘合代码的数量,可以为参数__FILE____LINE__创建单个结构,并将其作为一个参数传递给构造函数。

答案 1 :(得分:0)

  

问题是,预处理器不知道或不关心您是在调用SetString还是定义SetString重载。

显然,使用预处理器的原因是它忽略了命名空间。

一个好方法是咬紧牙关并重新定位整个大型应用程序,以使用不同的命名空间api_wrapped_ns而不是api_ns

api_wrapped_ns内,可以提供内联函数,在api_ns中包含具有相同签名的对应函数。

甚至可以有这样的编译时间开关:

namespace api_wrapped_ns {
#ifdef CONFIG_API_NS_WRAPPER
  inline long GetAmount(short Idx, const std::string& file, size_t line)
  {
    // of course, do more than just wrapping here
    return api_ns::GetAmount(Idx, file, line);     
  } 
  // other inlines
#else
  // Wrapping turned off: just bring in api_ns into api_wrapper_ns
  using namespace api_ns;
#endif
}

此外,包装可以零碎:

namespace api_wrapped_ns {
  // This function is wrapped;
  inline long GetAmount(short Idx, const std::string& file, size_t line)
  {
    // of course, do more than just wrapping here
    return
  }
  // The api_ns::FooBar symbol is unwrapped (for now)
  using api_ns::FooBar;
}