2013-04-25 52 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");