是否可以自动将值包装在其生命周期延伸到整个语句的临时值中?
最初,我希望我的问题的解决方案或替代方案会出现while writing the details这个问题,不幸的是没有发生,所以......
我有一个抽象基类Logger
,它提供了一个类似流的接口来生成日志语句。给定此类的实例logger
,我希望以下内容:
logger << "Option " << variable << " is " << 42;
与常规流不同,常规流只是从所有组件生成一个字符串(上例中的4个组件),我想生成一个类Statement
的实例,它管理所有语句组件的链表。然后,整个语句通过纯虚方法传递给从Logger
派生的类,它可以迭代语句的所有组件并对它们执行任何操作,包括获取有关其类型的信息,检索其值或转换他们到一个字符串。
棘手的一点:我想在没有动态内存分配的情况下完成上述操作。这意味着语句的每个组件都必须由一个临时类型包装,该类型将组件链接到语句范围内的可遍历列表中!
I posted a working example on ideone,有一个问题:每个组件都需要通过函数调用来包装,以便生成临时类型的实例。因此,日志语句最终看起来像这样:
logger << wrap("Option ") << wrap(variable) << wrap(" is ") << wrap(42);
我所有试图摆脱wrap
函数的尝试(例如,使用组件的隐式转换构造函数)到目前为止都失败了,因此这个问题。
如何自动将日志语句的组件包装在其组件类型中(例如,使用组件的转换构造函数),而无需显式调用包装函数?
或者,我希望获得有关实现相同效果的其他方法的建议,即允许在从logger派生的类中迭代日志语句的组件,而无需动态内存分配。
参考:ideone上的完整代码:
#include <iostream>
#include <sstream>
struct Statement;
struct Logger;
struct ComponentBase;
//------------------------------------------------------------------------------
struct ComponentBase {
mutable ComponentBase *next;
ComponentBase() : next(nullptr) { }
virtual std::string toString() = 0;
};
template <typename T>
struct Component : ComponentBase {
T value;
Component(T value) : value(value) { }
~Component() { }
virtual std::string toString() {
std::stringstream ss;
ss << value;
return ss.str();
}
};
struct ComponentIterator {
ComponentBase *ptr;
ComponentIterator(ComponentBase *ptr) : ptr(ptr) { }
ComponentBase &operator*() { return *ptr; }
void operator++() { ptr = ptr->next; }
bool operator!=(ComponentIterator &other) { return (ptr != other.ptr); }
};
//------------------------------------------------------------------------------
struct Statement {
Logger *logger;
ComponentBase *front;
ComponentBase *back;
ComponentIterator begin() { return front; }
ComponentIterator end() { return nullptr; }
template <typename T>
Statement(Logger &logger, Component<T> &component)
: logger(&logger), front(&component), back(&component) { }
~Statement();
template <typename T>
Statement &operator<<(Component<T> &&component) {
back->next = &component;
back = &component;
return *this;
}
};
//------------------------------------------------------------------------------
struct Logger {
template <typename T>
Statement operator<<(Component<T> &&component) {
return {*this, component};
}
virtual void log(Statement &statement) = 0;
};
Statement::~Statement() {
logger->log(*this);
}
//------------------------------------------------------------------------------
template <typename T>
Component<T const &> wrap(T const &value) {
return value;
}
template <size_t N>
Component<char const *> wrap(char const (&value)[N]) {
return value;
}
//------------------------------------------------------------------------------
struct MyLogger : public Logger {
virtual void log(Statement &statement) override {
for(auto &&component : statement) {
std::cout << component.toString();
}
std::cout << std::endl;
}
};
int main() {
std::string variable = "string";
MyLogger logger;
logger << wrap("Option ") << wrap(variable) << wrap(" is ") << wrap(42);
}
答案 0 :(得分:2)
我有一些疯狂但有效的解决方案。
如果像这样实现了组件,您将在代码中删除模板:
struct Component
{
mutable Component *next;
typedef std::function<std::string()> ToStringFunction;
ToStringFunction toString; // <-- 1
template<typename T>
Component(const T& value)
: next(nullptr),
toString(nullptr)
{
toString = [&value](){
std::stringstream ss;
ss << value;
return ss.str();
};
}
};
其中(1)是知道该怎么做的事。该成员std::function
是一个优化空间。
代码的其余部分应如下所示:
struct ComponentIterator {
Component *ptr;
ComponentIterator(Component *ptr) : ptr(ptr) { }
Component &operator*() { return *ptr; }
void operator++() { ptr = ptr->next; }
bool operator!=(ComponentIterator &other) { return (ptr != other.ptr); }
};
//------------------------------------------------------------------------------
struct Statement {
Logger *logger;
Component *front;
Component *back;
ComponentIterator begin() { return front; }
ComponentIterator end() { return nullptr; }
Statement(Logger &logger, Component &component)
: logger(&logger), front(&component), back(&component) { }
~Statement();
Statement &operator<<(Component &&component) {
back->next = &component;
back = &component;
return *this;
}
};
//------------------------------------------------------------------------------
struct Logger {
Statement operator<<(Component &&component) {
return{ *this, component };
}
virtual void log(Statement &statement) = 0;
};
Statement::~Statement() {
logger->log(*this);
}
//------------------------------------------------------------------------------
struct MyLogger : public Logger {
virtual void log(Statement &statement) override {
for (auto &&component : statement) {
std::cout << component.toString();
}
std::cout << std::endl;
}
};
int main() {
std::string variable = "string";
MyLogger logger;
//logger << wrap("Option ") << wrap(variable) << wrap(" is ") << wrap(42);
logger << 42;
logger << variable << " is " << 42;
logger << "Option " << variable << " is " << 42;
}
这将打印:
42
字符串是42
选项字符串是42
UPD
这里建议的dyp是没有lambda的Component
结构的替代实现:
struct Component
{
mutable Component *next;
void* value;
std::string toString(){
return _toString(this);
}
template<typename T>
Component(const T& inValue)
: next(nullptr),
value((void*)&inValue),
_toString(toStringHelper<T>)
{}
private:
typedef std::string(*ToStringFunction)(Component*);
ToStringFunction _toString;
template<typename T>
static std::string toStringHelper(Component* component)
{
const T& value = *(T*)component->value;
std::stringstream ss;
ss << value;
return ss.str();
}
};
答案 1 :(得分:1)
我提出了一个解决方案元组:
template <class... Ts> class streamTuple;
struct Logger {
template <typename T>
streamTuple<T> operator<<(const T& t);
template <typename Tuple, std::size_t ... Is>
void dispatch(const Tuple& tup, std::index_sequence<Is...>)
{
int dummy[] = {0, (void(std::cout << std::get<Is>(tup) << " "), 0)...};
static_cast<void>(dummy); // Avoid unused variable warning
}
// Logger can take generic functor to have specific dispatch
// Or you may reuse your virtual method taking ComponentBase.
};
template <class... Ts> class streamTuple
{
public:
streamTuple(Logger* logger, const std::tuple<Ts...>& tup) :
logger(logger), tup(tup) {}
streamTuple(streamTuple&& rhs) : logger(rhs.logger), tup(std::move(rhs.tup))
{
rhs.logger = nullptr;
}
~streamTuple()
{
if (logger) {
logger->dispatch(tup, std::index_sequence_for<Ts...>());
}
}
template <typename T>
streamTuple<Ts..., const T&> operator << (const T& t) &&
{
auto* moveddLogger = logger;
logger = nullptr;
return {moveddLogger, std::tuple_cat(tup, std::tie(t))};
}
private:
Logger* logger;
std::tuple<Ts...> tup;
};
template <typename T>
streamTuple<T> Logger::operator<<(const T& t) {
return {this, t};
}
用法:
int main() {
Logger log;
std::string variable = "string";
log << variable << 42 << "hello\n";
}