C++ 跟踪 class 中的更改
C++ Keep tracks of changes inside a class
想象一个 class 代表一封邮件 :
class mail {
string subject;
string content;
date receivedDate;
};
现在我想要实现的是知道我的邮件数据是否已设置,一旦设置,哪些发生了变化。我可以像这样组合 std::optional 和 std::map :
class Mail {
std::optional<string> subject;
std::optional<string> content;
std::optional<date> receivedDate;
enum EField { Subject, Content, ReceivedDate };
typedef std::map<EField, bool> ChangedMap;
ChangedMap changedFields;
public:
Mail(optional<string> subject, ... ) {
// initialize map with fields... hard coded
}
bool HasSubject() const { return subject; }
string GetSubject() const { return subject.get(); }
void SetSubject(const std::string& newSubject) {
subject = newSubject;
changedFields[Subject] = true;
}
void RemoveSubject() {
changedFields[Subject] = HasSubject();
subject.reset();
}
bool IsSubjectChanged() const {
return changedFields[Subject];
}
};
但我真的认为我在这里遗漏了一些重要的东西。您能找到更好的方法吗,最好是使用更少的内存并且没有硬编码值?
我考虑过从 std::optional 继承,但我也不认为这是一件好事。
谢谢
让我们概括一下这个问题:给定一个类型 T
,我想要一个包装器 tracked<T>
来跟踪 reads/writes 在 [=32= 的历史记录].
我会通过使用 std::tuple
和元编程来解决这个问题。首先,让我们根据 std::tuple
:
来定义 mail
class mail
{
private:
std::tuple<string, string, date> _data;
public:
// `variant_type` could be automatically computed from the
// tuple type.
using variant_type = std::variant<string, string, date>;
enum class index
{
subject = 0,
content = 1,
date = 2
};
template <index TIndex>
decltype(auto) access()
{
return std::get<static_cast<std::size_t>(TIndex)>(_data);
}
};
然后我会创建类似 tracked<T>
的东西来跟踪在 T
上执行的操作:
template <typename T>
class tracked
{
private:
using index_type = typename T::index;
using variant_type = typename T::variant_type;
struct write_action
{
variant_type _before;
variant_type _after;
};
struct read_action
{
index_type _index;
};
T _data;
std::vector<std::variant<write_action, read_action>> _history;
public:
template <index TIndex>
const auto& read() const
{
_history.emplace_back(read_action{TIndex});
return _data.access<TIndex>();
}
template <index TIndex, typename T>
void write(T&& new_value) const
{
// Remember previous value.
variant_type _before{_data.access<TIndex>()};
_history.emplace_back(write_action{_before, new_value});
return _data.access<TIndex>() = std::forward<T>(new_value);
}
};
上面的代码并不完全正确,因为您需要 action 类型的构造函数、异常处理、移动语义支持等等。不过,我希望您能大致了解一下。
想象一个 class 代表一封邮件 :
class mail {
string subject;
string content;
date receivedDate;
};
现在我想要实现的是知道我的邮件数据是否已设置,一旦设置,哪些发生了变化。我可以像这样组合 std::optional 和 std::map :
class Mail {
std::optional<string> subject;
std::optional<string> content;
std::optional<date> receivedDate;
enum EField { Subject, Content, ReceivedDate };
typedef std::map<EField, bool> ChangedMap;
ChangedMap changedFields;
public:
Mail(optional<string> subject, ... ) {
// initialize map with fields... hard coded
}
bool HasSubject() const { return subject; }
string GetSubject() const { return subject.get(); }
void SetSubject(const std::string& newSubject) {
subject = newSubject;
changedFields[Subject] = true;
}
void RemoveSubject() {
changedFields[Subject] = HasSubject();
subject.reset();
}
bool IsSubjectChanged() const {
return changedFields[Subject];
}
};
但我真的认为我在这里遗漏了一些重要的东西。您能找到更好的方法吗,最好是使用更少的内存并且没有硬编码值?
我考虑过从 std::optional 继承,但我也不认为这是一件好事。
谢谢
让我们概括一下这个问题:给定一个类型 T
,我想要一个包装器 tracked<T>
来跟踪 reads/writes 在 [=32= 的历史记录].
我会通过使用 std::tuple
和元编程来解决这个问题。首先,让我们根据 std::tuple
:
mail
class mail
{
private:
std::tuple<string, string, date> _data;
public:
// `variant_type` could be automatically computed from the
// tuple type.
using variant_type = std::variant<string, string, date>;
enum class index
{
subject = 0,
content = 1,
date = 2
};
template <index TIndex>
decltype(auto) access()
{
return std::get<static_cast<std::size_t>(TIndex)>(_data);
}
};
然后我会创建类似 tracked<T>
的东西来跟踪在 T
上执行的操作:
template <typename T>
class tracked
{
private:
using index_type = typename T::index;
using variant_type = typename T::variant_type;
struct write_action
{
variant_type _before;
variant_type _after;
};
struct read_action
{
index_type _index;
};
T _data;
std::vector<std::variant<write_action, read_action>> _history;
public:
template <index TIndex>
const auto& read() const
{
_history.emplace_back(read_action{TIndex});
return _data.access<TIndex>();
}
template <index TIndex, typename T>
void write(T&& new_value) const
{
// Remember previous value.
variant_type _before{_data.access<TIndex>()};
_history.emplace_back(write_action{_before, new_value});
return _data.access<TIndex>() = std::forward<T>(new_value);
}
};
上面的代码并不完全正确,因为您需要 action 类型的构造函数、异常处理、移动语义支持等等。不过,我希望您能大致了解一下。