在语句的持续时间内自动换行值

时间:2016-05-10 10:13:43

标签: c++ c++11

是否可以自动将值包装在其生命周期延伸到整个语句的临时值中?

最初,我希望我的问题的解决方案或替代方案会出现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);
}

2 个答案:

答案 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};
}

Demo

用法:

int main() {
    Logger log;
    std::string variable = "string";
    log << variable << 42 << "hello\n";
}