2013-04-25 32 views
1

如何製作通過引用進行比較但強制其無法在函數中創建的類型(防止引用堆棧/已刪除對象)。如何製作可輕鬆通過參考進行比較的類型

我想出了下面的Error類型,併爲它感到自豪,直到我意識到你可以在函數內部「返回Error :: New(...)」。問題出在h()函數中。

#include <stdio.h> 
#include <string.h> 
#include <string> 

using namespace std; 

class Error { 
    std::string _str; 
    const Error &from; 

    Error(); 
    Error(const char *s) 
     : _str(s), from(*this) 
    { 
    } 

public: 
    Error(const Error &err) 
     : from(err.from) 
    { 
    } 

    static Error New(const char *s) { 
     return Error(s); 
    } 

    bool operator== (const Error &rhs) const { 
     return (&from == &rhs.from); 
    } 

    bool operator!= (const Error &rhs) const { 
     return (&from != &rhs.from); 
    } 

    std::string ToString() { 
     return from._str; 
    } 

public: 
    static const Error None; 
}; 

const Error Error::None("none"); 

// user errors 
auto ErrConnect = Error::New("failed to connect"); 
auto ErrWrite = Error::New("invalid write"); 

Error f() { 
    return ErrConnect; 
} 

Error g() { 
    return Error::None; 
} 

Error h() { 
    return Error::New("test"); 
} 

int main() 
{ 
    printf("ErrConnect == ErrConnect : %d\n", ErrConnect == ErrConnect); 
    printf("ErrConnect == ErrWrite : %d\n", ErrConnect == ErrWrite); 
    printf("f() == ErrConnect : %d\n", f() == ErrConnect); 
    printf("f() == ErrWrite : %d\n", f() == ErrWrite); 
    printf("f() != ErrConnect : %d\n", f() != ErrConnect); 
    printf("f() != ErrWrite : %d\n", f() != ErrWrite); 
    printf("f() == Error::None : %d\n", f() == Error::None); 
    printf("f() != Error::None : %d\n", f() != Error::None); 
    printf("g() == Error::None : %d\n", g() == Error::None); 
    printf("g() != Error::None : %d\n", g() != Error::None); 
    printf("f().ToString() : %s\n", f().ToString().c_str()); 
    printf("ErrConnect.ToString() : %s\n", ErrConnect.ToString().c_str()); 

    auto err = f(); 
    auto err2 = err; 
    auto err3 = err2; 
    printf("err3 == ErrConnect : %d\n", err3 == ErrConnect); 

    auto err4 = h(); 
    printf("err4 from h() : %s\n", err4.ToString().c_str()); 
} 
+0

在引用變量中&只有在聲明時使用&in,如果您要比較兩個地址。 [指針和引用變量之間的區別](http://stackoverflow.com/questions/15995463/in-function-declaration-return-type/15995482#15995482) – 2013-04-25 17:23:04

回答

1

我發現,最好的方式來實現,這是使用靜態的std ::原子計數器(具體_uint_fast64_t似乎最好的),從該是爲創建IDS在錯誤的每個錯誤類型(常量字符* S)構造和std ::原子:: fetch_add()來抓取/增加它:

http://en.cppreference.com/w/cpp/atomic/atomic

唯一的缺點是,這些僅僅是在C +本+11(Visual Studio 2012; Linux不應該是一個問題除了舊發行版以外)。

1

允許在全局範圍創建某些內容,但不能在函數中創建。

跟蹤原始Error&對於您正在進行的任何操作不是必需的。相反,你需要創建一個錯誤來創建一個唯一的標記,並且所有的Error都被複制或從中移除以攜帶該標記。該令牌是Error& this不是必需的,但該令牌的生命週期需要延長從原始複製的所有Error的壽命。

一種方法是使用類型標記,可能是宏輔助來生成錯誤。小心謹慎,令牌可以被安排爲獨一無二的,不需要額外的工作。

舉個例子:

struct ErrorToken { 
    virtual std::string Description() const = 0; 
    ~ErrorToken() {} 
}; 
template<typename T> 
struct ErrorTokenImpl:ErrorToken { 
    virtual std::string Description() const /* final override if C++11 */ { 
    return T::desc(); 
    } 
}; 
class Error { 
    ErrorToken* token; 
    template<typename T> 
    static ErrorToken* get_token() { 
    static std::unique_ptr<ErrorToken> retval(new ErrorTokenImpl<T>()); 
    return retval.get(); 
    } 
public: 
    template<typename T> 
    Error(): token(get_token<T>()); 
    bool operator==(Error const& o) const { return token == o.token; } // etc 
    std::string GetDescription() const { 
    return token->Description(); 
    } 
}; 

#define MAKE_ERROR(Y, X) Error< struct Y { static const char* desc() { return X; }; } >() 
const Error ErrConnect = MAKE_ERROR(Connection, "failed to connect"); 

現在,任何人都可以在任何上下文中的錯誤,但錯誤的每個創作有一個標籤和一個字符串,並創建token將持續到靜態對象的清理時間。

+0

謝謝,這是一個有趣的想法,但我想擺脫與錯誤類型/錯誤代碼相關的簿記。 – ctn 2013-04-26 15:38:47

+0

@ctn注意到如果你創建了全局的'Error'值,每個值都由一個不同的「類型」和字符串構成,它們就可以用作'return ErrorBlah'; - 你可以創建一個本地錯誤代碼其中一個在函數中帶有MAKE_ERROR宏(假設對本地類型模板參數有足夠的C++ 11支持),但與原始設計不同,它不會調用未定義的行爲。 – Yakk 2013-04-26 16:03:50

0

我最終使用UUID的:

#include <boost/uuid/uuid.hpp> 
#include <boost/uuid/uuid_generators.hpp> 

using namespace std; 

class Error { 
    std::string msg; 
    boost::uuids::uuid tag; 

public: 
    Error(const char *s) 
     : msg(s), tag(boost::uuids::random_generator()()) 
    { 
    } 

    Error(const Error &rhs) 
     : msg(rhs.msg), tag(rhs.tag) 
    { 
    } 

    Error(Error &&rhs) 
     : msg(std::move(rhs.msg)), tag(rhs.tag) 
    { 
    } 

    Error &operator=(const Error &rhs) { 
     msg = rhs.msg; 
     tag = rhs.tag; 
     return *this; 
    } 

    bool operator==(const Error &rhs) const { 
     return (tag == rhs.tag); 
    } 

    bool operator!=(const Error &rhs) const { 
     return (tag != rhs.tag); 
    } 

    std::string ToString() { 
     return msg; 
    } 

public: 
    static const Error None; 
}; 

const Error Error::None("none"); 

// user errors 
auto ErrConnect = Error("failed to connect"); 
auto ErrWrite = Error("invalid write"); 
相關問題