1

tl; dr:有沒有方法可以將當前範圍的默認參數添加到C++中的所有隱式構造函數中?轉換構造函數的隱式參數

我目前正在爲C++中的嵌入式語言設計一個接口。目標是創建類型安全和方便的句法正確的表達式。現在,我認爲學習像boost :: proto這樣的重量級實現會在開發過程中產生太大的延遲,所以我試圖推出我自己的實現。

這裏是一個小的演示:

#include <iostream> 
#include <string> 
#include <sstream> 

class ExprBuilder 
{ 
public: 
    ExprBuilder(const int val) : val(std::to_string(val)) {} 
    ExprBuilder(const std::string val) : val(val) {} 
    ExprBuilder(const char* val) : val(val) {} 

    ExprBuilder(const ExprBuilder& lhs, const ExprBuilder& arg) { 
     std::stringstream ss; 
     ss << "(" << lhs.val << " " << arg.val << ")"; 
     val = ss.str(); 
    } 

    const ExprBuilder operator()(const ExprBuilder& l) const { 
     return ExprBuilder(*this, l); 
    } 

    template<typename... Args> 
    const ExprBuilder operator()(const ExprBuilder& arg, Args... args) const 
     { 
      return (*this)(arg)(args...) ; 
     } 

    std::string val; 
}; 

std::ostream& operator<<(std::ostream& os, const ExprBuilder& e) 
{ 
    os << e.val; 
    return os; 
} 

int main() { 
    ExprBuilder f("f"); 
    std::cout << f(23, "foo", "baz") << std::endl; 
} 

正如你所看到的,它是相當簡單的使嵌入由於C表達式++重載和隱式轉換。

但是我面臨着一個實際問題:在上面的例子中,所有的數據都是以std :: string對象的形式進行分配的。在實踐中,我需要一些更復雜的東西(AST節點),它們分配在堆上並由專用所有者管理(遺留代碼,不能更改)。所以我必須通過一個獨特的論點(所有者)並將其用於分配。我寧願不在這裏使用靜態字段。

我正在尋找的是一種使用方法,每次使用建造者時都要求用戶提供這樣的所有者,但方式很簡單。像動態範圍的變量會很棒。有沒有辦法在C++中獲得以下內容:

class ExprBuilder 
{ 
    ... 
    ExprBuilder(const ExprBuilder& lhs, const ExprBuilder& arg) { 
     return ExprBuilder(owner.allocate(lhs, rhs)); // use the owner implicitly 
    } 
    ... 
}; 

int main() { 
    Owner owner; // used in all ExprBuilder instances in the current scope 
    ExprBuilder f("f"); 
    std::cout << f(23, "foo", "baz") << std::endl; 
} 

這可能嗎?

編輯:我想澄清我爲什麼(到目前爲止)不考慮全局變量。業主必須在某個時候由建築商的用戶手動發佈,因此我無法創建一個專案。因此,用戶可能完全「忘記」所有者。爲了避免這種情況,我正在尋找一種方式來強化typechecker的所有者的存在。

+0

我想你說的是某種形式的'全球variable' –

+0

您可以設置'Owner' RAII靜態/全局變量,並使用'ExprBuilder'是全局變量。 – Jarod42

+0

您的意思是使用unique_ptr作爲全局變量以確保它僅用於上下文中並且必須明確保留?如果指針被用戶遺忘,會不會產生運行時錯誤? – choeger

回答

1

沒有全局/靜態變量,這幾乎是不可能的,因爲沒有全局/靜態信息,局部變量Owner ownerExprBuilder f無法知道任何關於對方的任何信息。

我認爲最乾淨的方法是將一個

static Owner* current_owner; 

添加到ExprBuilder類。然後你可以添加一個新類ScopedCurrentOwnerLock,它在構造函數中設置current_owner,並在析構函數中將其設置爲nullptr。然後,你可以用它類似於一個互斥鎖:

class ScopedCurrentOwnerLock { 
public: 
    ScopedCurrentOwnerLock(Owner const& owner) { 
     ExprBuilder::current_owner = &owner; 
    } 
    ~ScopedCurrentOwnerLock() { 
     ExprBuilder::current_owner = nullptr; 
    } 
}; 

int main() { 
    Owner owner; 
    ScopedCurrentOwnerLock lock(owner); 
    ExprBuilder f("f"); 
} 

如果您有機會獲得Owner代碼,你可以省略的Owner構造函數/析構函數的類ScopedCurrentOwnerLock直接設置/取消指針。

請注意以下兩個問題的解決方案:

  • 如果業主超出範圍鎖超出範圍之前,你有一個無效的指針。

  • 如果您同時有多個鎖,靜態指針具有不可預知的行爲, G。由於多線程。

0

你所有的ExprBuildersOwner一個扶養,和你正確不想全局狀態。所以你必須將所有者傳遞給每個構造函數。

如果你真的不想將owner,添加到塊中的所有實例,你可以創建一個工廠來爲你傳遞它。

struct ExprBuilderFactory 
{ 
    Owner & owner; 
    ExprBuilder operator()(int val) { return ExprBuilder(owner, val); } 
    ExprBuilder operator()(char * val) { return ExprBuilder(owner, val); } 
    // etc 
} 

int main() { 
    Owner owner; 
    ExprBuilderFactory factory{ owner }; 
    ExprBuilder f = factory("f"); 
}