2013-02-21 65 views
0

我试图做一个奇特的宏来获得一些调试信息:你现在的范围的名字!这可以通过例如一个断言。我试图使它递归:如何递归访问父范围?

// Global namespace 
struct ScopeDef{ static const char* GetName() {return "";} }; 
typedef ScopeDef ScopeDefParent; 

// Macro to place into your namespace/scope 
#define NG_SCOPEDEF(scopename) \ 
    struct ScopeDef { \ 
     static const char* GetName() {return scopename;} \ 
     typedef ScopeDefParent Parent; \ 
    }; \ 
    typedef ScopeDef ScopeDefParent; 

,并用它喜欢:

// Recursive template for testing 
template< class T > void PrintImpl() { 
    PrintImpl<T::Parent>(); 
    printf("::%s", T::GetName()); 
} 
template<> void PrintImpl<::ScopeDef>() {} 
template< class T > void PrintS() { PrintImpl<T>(); printf("\n");} 

// Lets try it: 
namespace First { 
    NG_SCOPEDEF("First"); 
    namespace Second { 
     NG_SCOPEDEF("Second"); 
     static void AFun() { 
      // This prints "::First::Second" 
      PrintS<ScopeDef>(); 
     } 
    } 
    struct Third { 
     NG_SCOPEDEF("Third"); 
     static void BFun() { 
      // This is endless recursion 
      PrintS<ScopeDef>(); 
     } 
    }; 
} 

它不会在类范围工作,因为定义的顺序并不重要。

这不是一个很好的解决方案。那么有没有办法以某种方式访问​​父范围?在常规代码中,我只会限定(“:: First :: ScopeDef”),但这对于宏来说没有任何意义。

+0

你有一个很好的理由不使用__FILE__和__LINE__其粗略地讲给出相同的信息? – stijn 2013-02-21 16:42:08

+0

他们是有帮助的,是的,但不一样。我脑海中的大部分东西都会像上面的模板PrintS <>一样使用结构。 – Borph 2013-02-21 16:46:08

回答

1

你可以做这样的事情在C++中,你把宏每当你打开一个范围,让析构函数采取清理时范围退出的照顾。这个例子将把完整的范围打印到stderr,并且这个代码的输出如下。

main 
main::First 
main::First::Second 
main::First::Second::DummyClass::DummyFunction 
main::First 
main 

下面是代码:

#include <stdio.h> 

class NG_SCOPE_CLASS; 

NG_SCOPE_CLASS* NG_SCOPE_END = 0; 

class NG_SCOPE_CLASS 
{ 
public: 
    NG_SCOPE_CLASS(const char* scope) 
    { 
     _scope = scope; 
     _prev = NG_SCOPE_END; 
     NG_SCOPE_END = this; 
    } 
    ~ NG_SCOPE_CLASS() 
    { 
     NG_SCOPE_END = _prev; 
    } 
    void PrintScope() 
    { 
     if(_prev) 
     { 
      _prev->PrintScope(); 
      fprintf(stderr, "::"); 
     } 
     fprintf(stderr, "%s", _scope); 
    } 
private: 
    NG_SCOPE_CLASS* _prev; 
    const char* _scope; 
}; 

#define NG_SCOPE_PRINT { if(NG_SCOPE_END) { NG_SCOPE_END->PrintScope(); fprintf(stderr, "\n"); } } 
#define NG_SCOPE(X) NG_SCOPE_CLASS _NG_SCOPE_CLASS(X) 

// THAT'S IT FOR THE DEFINITIONS ABOVE, BELOW IS JUST SOME SAMPLE CODE. 

class DummyClass 
{ 
public: 
    void DummyFunction() 
    { 
     NG_SCOPE("DummyClass::DummyFunction"); 
     NG_SCOPE_PRINT; 
    } 
}; 

int main(int argc, const char* argv[]) 
{ 
    NG_SCOPE("main"); 
    NG_SCOPE_PRINT; 
    { 
     NG_SCOPE("First"); 
     NG_SCOPE_PRINT; 
     { 
      NG_SCOPE("Second"); 
      NG_SCOPE_PRINT; 
      DummyClass theDummyInstance; 
      theDummyInstance.DummyFunction(); 
     } 
     NG_SCOPE_PRINT; 
    } 
    NG_SCOPE_PRINT; 
} 
+0

+1不是一个坏主意。看起来像增强日志库使用什么。看起来你并不需要宏,只需要使用函数就可以做到这一点? – stijn 2013-02-21 17:06:51

+0

其基本思想是堆栈上的东西的析构函数与范围内在关联,所以如果你可以在每个范围内得到一个局部变量,剩下的就由C++正确地完成了。 (不要把两个这样的变量放在同一个作用域中!)所以你不能用*只是*函数来实现它,你需要堆栈上的对象。无论你是否用宏实现它,取决于你,这只是一个例子。 – wilsonmichaelpatrick 2013-02-21 17:21:34

+0

这种方法是使用一个全局变量(不是线程安全的)并继承对象的构造,这不可能为名称空间提供名称或类范围,就像我尝试过的那样。 另一件事是,你现在DummyFunction低于“主::首先::二::”这不是由我意。好吧,并非总是如此,我同意在调用者的范围内出现是很好的。 – Borph 2013-02-22 14:08:21

0

为了完整起见,我们的工作解决方案:

#if defined(_MSC_VER) 
    // GCC solution below. Note MSVC gives warning about unused typedefs but can be suppressed. 
    #define NG_SCOPEDEF(scopename) \ 
     struct ScopeDefTag { static const char* Name(){return (scopename);}; }; \ 
     typedef ::Scopes::Impl< ScopeDefTag, ScopeDef > ScopeDefHelper; \ 
     struct ScopeDef : ScopeDefHelper {} 
#else 
    // GCC seems to not look ahead. 
    #define NG_SCOPEDEF(scopename) \ 
     struct ScopeDefTag { static const char* Name(){return (scopename);}; struct SDH : ::Scopes::Impl< ScopeDefTag, ScopeDef >{}; }; \ 
     struct ScopeDef : ScopeDefTag::SDH {} 
#endif 

namespace Scopes { 
struct Chain { 
    const char* const m_Lit; 
    const Chain* const m_Prev; 
    Chain(const char* lit, const Chain* prev) :m_Lit(lit), m_Prev(prev) {} 
}; 

template< bool DMY = true > 
struct RootScopeDef 
{ 
    static const Chain chain; 
    static const Chain* Get() { return &chain; } 
}; 
// Being template just to have this in header: 
template< bool DMY > const Chain RootScopeDef<DMY>::chain = Chain("", 0); 


template< class TAG, class PARENT > 
struct Impl { 
    static const Chain chain; 
    static const Chain* Get() { return &chain; } 
    typedef PARENT Parent; 
}; 

template< class TAG, class PARENT > 
const Chain Impl<TAG, PARENT>::chain = Chain(TAG::Name(), &PARENT::chain); 

} // namespace 

// Global namespace 
typedef Scopes::RootScopeDef<> ScopeDef; 

它是基于在编译器中的漏洞和不符合标准的! MSVS认为用作模板参数的ScopeDef不是下面的那个,因为它取决于那个非常typedef,因此它解析为父类,这将在后面被映射。这也适用于将宏放置在模板中时,因为MSVS懒惰地实例化它们。 GCC反而似乎只是没有向前看,并将SDH的基础解决为正确的基础。 MSVS会在这里产生无限循环的m_Prev引用。

底线:这给你一个很好的方式打印出来命名空间和类进行调试,而且作为类型例如模板专业化!