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

C++ 对于外部API的运行时跟踪,是否有比预处理器重定向更好的替代方案?,c++,redirect,c-preprocessor,bug-tracking,C++,Redirect,C Preprocessor,Bug Tracking,我正试图解决一个棘手的问题。首先,概述: 我有一个不受我控制的外部API,它被大量遗留代码使用 如果只编写外部API来跟踪其自身的使用情况,那么遗留代码中有几类bug可能在运行时被检测到,但事实并非如此 我需要找到一个解决方案,使我能够将对外部API的调用重定向到跟踪API使用情况和记录错误的跟踪框架中 理想情况下,如果可能的话,我希望日志能够反映触发错误的API调用的文件和行号 下面是我想跟踪的一类错误的示例。我们使用的API有两个函数。我会叫他们GetAmount和SetAmount。它

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

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

  • 如果只编写外部API来跟踪其自身的使用情况,那么遗留代码中有几类bug可能在运行时被检测到,但事实并非如此
  • 我需要找到一个解决方案,使我能够将对外部API的调用重定向到跟踪API使用情况和记录错误的跟踪框架中
  • 理想情况下,如果可能的话,我希望日志能够反映触发错误的API调用的文件和行号
下面是我想跟踪的一类错误的示例。我们使用的API有两个函数。我会叫他们GetAmount和SetAmount。它们看起来像这样:

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

// Set an indexed amount
void SetAmount(short Idx, long amount);
// 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__))
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
}
这些是常规的C函数。我试图在运行时检测到的一个错误是,当使用尚未使用SetAmount设置的Idx调用GetAmount时

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

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

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

// Set an indexed amount
void SetAmount(short Idx, long amount);
// 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__))
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
}
然后,在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 include树中的任何代码

所以我想我现在有两个问题: 1:如何解决API过载问题? 2:有没有其他方法可以更好地完成我想做的事情


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

好吧,对于需要重载的情况,可以使用一个类实例来重载一些参数

#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);
      }
 };

这是非常伪代码,但我想你明白了。在遗留代码中,只要提到特定的函数名,它就会被函子对象替换,并且函数实际上是在函子上调用的。请考虑您只需要对过载有问题的函数执行此操作。为了减少粘合代码的数量,您可以为参数
\uuuuuu FILE\uuuuuuu
\uuuuuu LINE\uuuuuuu
创建一个结构,并将其作为一个参数传递到构造函数中

好的,对于需要重载的情况,可以使用一个类实例来重载一些参数

#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);
      }
 };
这是非常伪代码,但我想你明白了。在遗留代码中,只要提到特定的函数名,它就会被函子对象替换,并且函数实际上是在函子上调用的。请考虑您只需要对过载有问题的函数执行此操作。为了减少粘合代码的数量,您可以为参数
\uuuuuu FILE\uuuuuuu
\uuuuuu LINE\uuuuuuu
创建一个结构,并将其作为一个参数传递到构造函数中

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

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

一个好的方法是咬紧牙关,重新定位整个大型应用程序,以使用不同的名称空间
api\u-wrapped\u-ns
,而不是
api\u-ns

api\u-wrapped\u-ns
内部,可以提供内联函数,这些函数在
api\u-ns
中用类似的签名包装对应的函数

甚至可以有这样一个编译时开关:

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

// Set an indexed amount
void SetAmount(short Idx, long amount);
// 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__))
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;
}
问题是,预处理器不知道或不关心您是调用SetString还是定义SetString重载

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

一个好的方法是咬紧牙关,重新定位整个大型应用程序,以使用不同的名称空间
api\u-wrapped\u-ns
,而不是
api\u-ns

api\u-wrapped\u-ns
内部,可以提供内联函数,这些函数在
api\u-ns
中用类似的签名包装对应的函数

甚至可以有这样一个编译时开关:

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

// Set an indexed amount
void SetAmount(short Idx, long amount);
// 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__))
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;
}

我很惊讶这没有得到回应。。。有什么不清楚的地方我需要解释吗?这个方法比我使用的方法要优雅得多()。谢谢你-我会用这个方法玩。我假设您可以使用printf样式的变量参数函数,并在该函数中定义相应的运算符()?看起来我仍然需要codeproject页面中的堆栈抓取器将变量参数从一个函数传递到另一个函数。但是,我仍然可以用它来处理重载。再次感谢。我很惊讶这没有得到回应。。。有什么不清楚的地方我需要解释吗?这个方法比我使用的方法要优雅得多()。谢谢你-我会用这个方法玩。我假设你可以用一个变量参数函数,形式是pri