1

如何创建一个通过引用进行比较但强制它不能在函数中创建的类型(防止对堆栈/已删除对象的引用)。

我想出了下面的错误类型并为此感到自豪,直到我意识到您可以在函数中执行“return 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());
}
4

3 回答 3

1

不允许在全局范围内创建某些东西,但不能在函数中创建。

Error&您所做的任何事情都不需要跟踪原件。相反,您希望创建一个错误来创建一个唯一的令牌,并且所有Error的 s 都从它复制或移动以携带该令牌。该令牌Error& this不是必需的,但该令牌的生命周期需要延长Error从原始复制的所有 s 的生命周期。

一种方法是使用类型标签(可能是宏辅助)来生成错误。小心地,令牌可以被安排为独一无二的,并且不需要在每个错误的基础上进行额外的工作。

举个例子:

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将持续到静态对象清理时间。

于 2013-04-25T17:44:31.810 回答
1

I've found that the best way to implement this is using a static std::atomic counter (specifically, _uint_fast64_t seems best) from which ids are created for each error type in the Error(const char *s) constructor and std::atomic::fetch_add() to fetch/increment it:

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

The only drawback is that these are only present in C++11 (Visual Studio 2012; Linux should not be a problem except for old distros).

于 2013-04-29T14:49:38.733 回答
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");
于 2013-04-26T15:37:08.080 回答