簡體   English   中英

訪問宏中的變量值

[英]Accessing variable values within a macro

前段時間,我為c和c ++程序制作了這個漂亮的斷言宏

#define ASSERT(truthy, message) \
     if (!(truthy)) \
     {\
         cout << message << " on line " << __LINE__ << " in file " << __FILE__ << ". Check was " << #truthy << endl;\
     }

Scatter ASSERT會在整個代碼中調用它,並且只要truthy值不真實,它就會發出警告! 在開發過程中非常方便,以提醒您潛在的錯誤。

ASSERT(filesFound > 0, "Couldn't find any files, check your path!");

當filesFound為0時,宏將打印出來

找不到任何文件,檢查你的路徑! 在文件openFiles.c的第27行。 檢查是filesFound> 0

現在我想要它打印,給我更多相關信息,是傳遞到truthy參數的任何變量的值。 像這樣

找不到任何文件,檢查你的路徑! 在文件openFiles.c的第27行。 檢查是filesFound> 0,filesFound是0

我想知道,這似乎是類似lisp的領域,是否有任何黑魔法c預處理可用於評估變量和函數的值,而不評估truthy聲明?

我假設很失望。

我一直使用的另一種解決方案是在宏中支持varargs,然后強制斷言用戶指定相關的消息/變量 - 每次都是一些額外的工作,但從好的方面來說,你可以完全得到您想要的格式,並包含“truthy”位中沒有的信息,例如:

#define ASSERT(truthy, message, ...) \
if (!(truthy)) \
{\
    MyAssertHandler(__LINE__, __FILE__, #truthy, message, ##__VA_ARGS__);
}

那么你的處理程序只是一個相當標准的var-arg函數,它可以使用例如vsnprintf生成消息並輸出它,例如我的頭頂:

void MyAssertHandler(int line, const char* file, const char* expressionStr, const char* format, ...)
{
    // Note: You probably want to use vsnprintf instead to first generate
    //       the message and then add extra info (line, filename, etc.) to
    //       the actual output 
    va_list args;
    va_start(args, format);
    vprintf(format, args);
    va_end(args);

    // Log to bug database, DebugBreak() if a debugger is attached, etc.
}

用法:

ASSERT(IsBlah(), "BlahBlah: x = %.2f, name = %s", GetX(), GetName());

我無法想象這樣做的方法......除了傳遞另一個參數

#define ASSERT_PARAM(truthy, message, param) \
     if (!(truthy)) \
     {\
         cout << message << " on line " << __LINE__ << " in file " << __FILE__ << ". Check was " << #truthy  << ", value was " << param << endl;\
     }

你會這樣使用它:

ASSERT_PARAM(filesFound > 0, "Couldn't find any files, check your path!", filesFound);

得到:

Couldn't find any files, check your path! on line 27 in file openFiles.c. Check was filesFound > 0, value was 0

你想要做的事聽起來非常復雜。 我擔心在C ++中它是不可能的。

從技術上講,您正在評估的是bool表達式,因此只要斷言失敗,您就可以將其傳遞給解析器。 解析器然后將構建表達式樹,獲取葉子(表達式的元素)並返回它們。 然后應打印出返回的值。 為此,您需要支持C ++ AFAIK實際上不支持的反射。

也許不是夢想的解決方案,但您可以將整個語句傳遞給宏。

#define ASSERT(trusty, action) if (!trusty) { action }

ASSERT(trusty, cout << a << b;)
ASSERT(trusty, printf("%d, %f\n", a, b);)

我想你可以分裂的truthy像他們這樣做的第一個答案表達在這里 ,然后你大概可以打印個人價值。 但我不確定它是否確實有效。

然后可以使用可變參數模板函數來進行打印

也許你可以妥協,只允許在斷言表達式中使用2個變量和1個運算符? 如果是這樣,您可以制作這樣的臨時解決方案:

#include <iostream>
#include <string>

#define STRINGIFY(x) #x

#define BIN_ASSERT(obj1, op, obj2, msg)                                 \
  if(!(obj1 op obj2))                                                   \
  {                                                                     \
    std::cout << msg << " on line " << __LINE__                         \
         << " in file " << __FILE__                                     \
         << "." << std::endl                                            \
         << "Check was "                                                \
         << STRINGIFY(obj1) STRINGIFY(op) STRINGIFY(obj2)               \
         << "." << std::endl                                            \
         << "Operator " << #obj1 << ": " << obj1                        \
         << "." << std::endl                                            \
         << "Operator " << #obj2 << ": " << obj2                        \
         << "." << std::endl;                                           \
  }


int main (void)
{
  int x = 2;
  int y = 3;
  std::string s1 = "hello";
  std::string s2 = "world";

  BIN_ASSERT(1, +, -1, "Value zero"); std::cout << std::endl;
  BIN_ASSERT(x, ==, y, "Numbers not equal"); std::cout << std::endl;
  BIN_ASSERT(s1, ==, s2, "Strings not equal"); std::cout << std::endl;
}

輸出:

Value zero on line 30 in file test.c.
Check was 1+-1.
Operator 1: 1.
Operator -1: -1.

Numbers not equal on line 31 in file test.c.
Check was x==y.
Operator x: 2.
Operator y: 3.

Strings not equal on line 32 in file test.c.
Check was s1==s2.
Operator s1: hello.
Operator s2: world.

我想知道宏是否接收消息真的很有用。 失敗的斷言是向開發人員發出的一條消息,即代碼中存在導致異常行為或將程序置於不可接受狀態的錯誤。 用戶與它的關系較少(如果他們甚至可以訪問源代碼)。

下面的代碼定義了一個ASSERT宏,它接受一個布爾表達式,對其進行求值並輸出一條信息性消息。 該消息包含您在斷言失敗時要求檢查的值。

宏,就像標准的assert()宏(在<cassert> )繼續調用abort() (來自<cstdlib> )來導致程序異常終止。 這就是你想要的,因為程序進入了一個不知道還要做什么的狀態。

我在這里使用std::printf()為了簡潔。 你做任何你想做的事。

#include <cstdlib>
#include <cstdio>

#define ASSERT(value, inspect)                                                 \
    if (!(value)) {                                                            \
        std::printf("ASSERTION FAILED: '%s', %s is %d: %s@%s:%d\n", #value,    \
                    #inspect, inspect, __func__, __FILE__, __LINE__);          \
        abort();                                                               \
    }

int foo() { return 42; }

int main()
{
    // ...
    ASSERT(foo() - 40 == 1, foo());
    //...
}

程序運行:

$ ./a.out
ASSERTION FAILED: 'foo() - 40 == 1', foo() is 42: main@prog.cc:16
Abort

如果不向宏添加更多參數,則無法完全按照您的要求執行操作。 在某些時候,你必須停下來意識到你花時間創建一個你不想看到的文本字符串。

您需要構建表達式“grabber”/ builder。

宏將變成如下:

#define ASSERT_PARAM(truthy, message, param) \
 if (!(truthy)) \
 {\
     Grabber g;
     g << #truthy; // grab expression as string
     g % truthy;  // grab expression and values
     cout << message << " on line " << __LINE__ << " in file " << __FILE__ << ". Check was " << #truthy  << ", value was " << param << endl;\
     cout << g; \
 }

Grabber做什么?

這是一堆瘋狂的C ++,它構建了一個表達式。 它會使每個操作員超載以“抓住”操作員的參數。 每個操作員都返回一個對抓取器的引用,因此它可以抓住下一個操作符。

Grabber g;
g % filesFound > 0;

由於%(和*和/)具有高優先級,因此上述解析如下:

((g % filesFound) > 0)

如果template<typename T> Grabber::operator%(T const & val)只記錄(或打印)傳入的值(即filesFound),並且 - 重要的是 - 返回自身(g)以使其成為下一個表達式的一部分:即它變為g> 0.導致template<typename T> Grabber::operator>(T const & val)被調用,並且> 0被記錄。

然后cout << g可以把所有被抓住的東西吐出來。

如上所述“有可能 - Catch庫可以做到這一點。但它真是太難了”。

PS你應該把你的宏包裹在一個do ...而0這樣:

#define ASSERT_PARAM(truthy, message, param) \
 do \
 { \
   if (!(truthy)) \
   {\
     cout << message << " on line " << __LINE__ << " in file " << __FILE__ << ". Check was " << #truthy  << ", value was " << param << endl;\
     cout << g; \
   } \
 } while (0)

你目前所擁有的意味着這是有效的代碼:

ASSERT(foo != 0)
else
{
}

這不是有效的代碼:

if (foo != nullptr)
   ASSERT(foo->bar != nullptr);
else
   x = 10;

令人驚訝的是,我之前解決了類似的問題,但我不確定它是否可以在這種情況下幫助你。

最初的解決方案是Andrei Alexandrescu在文章“ 增強斷言”中提出的,毫無疑問,它依賴於一些宏觀技巧。

這個驚人的設施可以用作以下內容:

string s1, s2;
...
SMART_ASSERT(s1.empty() && s2.empty())(s1)(s2);

如果出現問題,將顯示該消息

Assertion failed in matrix.cpp: 879412:
Expression: 's1.empty() && s2.empty()'
Values: s1 = "Wake up, Neo"
        s2 = "It's time to reload."

需要注意的是,SMART_ASSERT理論上可以捕獲無限變量。

有關實施細節,請查看文章。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM