【问题标题】:One Debug-Print function to rule them all一个调试打印功能来统治它们
【发布时间】:2011-04-15 12:59:09
【问题描述】:

我的代码中有几个不同的结构,我想打印到控制台。

三个例子(几百个):

typedef struct ReqCntrlT    /* Request control record */
{
int             connectionID;
int             dbApplID;
char            appDescr[MAX_APPDSCR];
int             reqID;
int         resubmitFlag;
unsigned int    resubmitNo;
char            VCIver[MAX_VCIVER];
int             loginID;

}   ReqCntrlT;

//---------------------------------------------   

typedef struct      /* Connection request data block */
{
    char            userID[MAX_USRID];
    char            password[MAX_PWDID];

}   CnctReqDataT;

//---------------------------------------------   

typedef struct {
    char            userID[LOGIN_MAX_USERID];
    char            closure;
    int             applVersion;
    int             authorizationDataLength;
    void            *authorizationData; }   LoginReqDataT;

所以我想要的是一个调试函数,它简单地将结构作为参数并输出结构的所有成员,如下所示:

LoginReqDataT* foo = new LoginReqDataT;
foo->applVersion = 123;
//...
debugPrintMe(foo);

CnctReqDataT* bar = new CnctReqDataT;
strcpy(bar->userID, "123");
strcpy(bar->password, "mypwd");
debugPrintMe(bar);

我目前拥有的是一个无穷无尽的功能,它正在做这样的事情:

template <class T>
void debugPrintMe(T myvar)
{
    if (!DEBUG) return;

    if (typeid(T) == typeid(ReqCntrlT*))
    {
        ReqCntrlT* r = (ReqCntrlT*)myvar; 
        cout << "reqControl: " << endl 
             << "\tconnectionID: " << r->connectionID << endl
             << "\tdbApplID: " << r->dbApplID << endl
             //...
             << "\tloginID: " << r->loginID << endl << endl;
    }
    else if (typeid(T) == typeid(CallBkAppDataT*))
    {
        CallBkAppDataT* c = (CallBkAppDataT*)myvar; 
        cout << "appData: " << endl
             << "\tappRespBlockSize " << c->appRespBlockSize << endl
             //...
             << "\tstreamType: " << c->streamType << endl << endl;
    }
    //... and so on
}

有没有更优雅的方法来做到这一点?

【问题讨论】:

  • 您是否考虑过在您的结构上使用 toString 方法?
  • @fosrvarir:我不允许修改任何结构

标签: c++ debugging templates casting struct


【解决方案1】:

是的,肯定有一种更优雅的方式来做到这一点(... else if (typeid(T) == ...?呸!)。你可以为你的structs 写一些operator &lt;&lt;()s。这使您的debugPrintMe() 函数既美观又通用,还允许您将结构流式传输到coutcerr、记录器、ostringstream、...

下面是一个帮助您入门的示例:

std::ostream& operator <<(std::ostream& os, const ReqCntrlT& r)
{
    os << "reqControl"
        << "\n\tconnectionID: " << r.connectionID 
        << "\n\tdbApplID: " << r.dbApplID 
        << "\n\tappDescr: " << r.appDescr
        << "\n\treqID: " << r.reqID
        << "\n\tresubmitFlag: " << r.resubmitFlag
        << "\n\tresubmitNo: " << r.resubmitNo
        << "\n\tVCIver: " << r.VCIver
        << "\n\tloginID: " << r.loginID
        << '\n';
    return os;
}

template <class T>
void debugPrintMe(const T& myvar)
{
    if (DEBUG)
    {
        std::cout << myvar << std::endl;
    }
}

int main()
{
    ReqCntrlT r;

    // [...]

    debugPrintMe(r);

    return 0;
}

【讨论】:

  • 谢谢,这是个好主意。只是想知道是否有可能自动迭代结构的所有成员以避免显式调用所有成员?
  • 唉,在 C++ 中没有与 Python 的 dir() 等效的东西。
【解决方案2】:

我不认为这在没有内置自省的语言中很容易实现,因此您最好为每个结构重载 operator&lt;&lt; 以将 thme 打印到 ostream。

【讨论】:

    【解决方案3】:

    我不会在typeid 上进行分支,而是使用一个非常基本的 C++ 特性,它没有任何运行时开销:函数重载!由于您正在编写代码以打印函数,因此只需将其分成单独的函数:

    void debugPrintMe(ReqCntrlT const& r){
      // ...
    }
    
    void debugPrintMe(CallBkAppDataT const& c){
      // ...
    }
    
    // others
    

    【讨论】:

    • 他这样做的方式,他确实需要强制转换,否则函数将无法编译,因为他在同一个函数中处理所有类型。
    • 其实,没有。他已经使用了模板,因此函数会针对每种输入类型进行实例化,并且函数内的大部分代码无论如何都被浪费了。
    • 那些不是静态的 if,如果 C++ 中存在这样的东西的话。当他尝试实例化模板时,编译器将查看每条语句,即使是永远不会执行的 if 块下的语句。如果[T = ReqCntrlT*],并且他没有强制转换而是直接使用myvar,当它到达myvar-&gt;appRespBlockSize时会出现编译错误,因为ReqCntrlT没有成员appRespBlockSize
    • @Benjamin:对不起,你完全正确,我忘记了编译器所做的代码分析。谢谢你再次提醒我。 :)
    【解决方案4】:

    由于您已经愿意使用检查 typeid(T) 的模板函数,您可能希望只专门化 TheOneTrueDebugFunction 的实现:

    template <class T>
    void debugPrintMe(const T& myvar);
    
    template <>
    void debugPrintMe< ReqCntrlT* >(T)
    {
       // implementation to print debug messages
    }
    
    template <>
    void debugPrintMe< CallBkAppDataT* >(T)
    {
       // implementation
    }
    

    正如您所注意到的,您必须使用 typeid(T)==typeid(XYZ*) 玩游戏才能让它打印正确的类型,以响应类型是 StructStruct*const Struct * 等等。您需要探索使用更通用的类型特征以避免更多重复代码。

    【讨论】:

      猜你喜欢
      • 2014-08-07
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-05-10
      • 2010-09-20
      • 2012-11-23
      相关资源
      最近更新 更多