std :: tuple args生命周期?

时间:2016-02-25 14:55:08

标签: c++ c++11 logging tuples

我正在实现一个将打印args保存到元组中的记录器。元组在记录器线程中使用。

class LogTransaction
{
public:
    typedef std::unique_ptr<LogTransaction> Ptr;

    LogTransaction() {}
    virtual ~LogTransaction() {}

    virtual void Log(std::FILE* file) = 0;
};

template<typename... Ts>
class LogTransaction : public LogTransaction
{
public:

    template<typename... Args>
    LogTransaction(Args&&... args) : m_args(std::forward<Args>(args)...) {}

    ~LogTransaction()
    {
        if (m_trd.joinable())
        {
            m_thrd.join();
        }
    }

    virtual void Log(std::FILE* file) override
    {
        Log(file, m_args);
    }

private:

    template <class T> T convert(const T& val) { return val; }
    const char* convert(const std::string& val) { return val.c_str(); }

    // Sequence generator
    template <std::size_t... Idx>
    struct index {};

    template <std::size_t N, std::size_t... Idx>
    struct gen_seq : gen_seq<N - 1, N - 1, Idx...> {};

    template <std::size_t... Idx>
    struct gen_seq<0, Idx...> : index<Idx...> {};

    template <typename... Args>
    void Log(std::FILE* file, std::tuple<Args...>& tup)
    {
       Log(file, tup, gen_seq<sizeof...(Args)>{});
    }

    template <typename... Args, std::size_t... Is>
    void Log(std::FILE* file, std::tuple<Args...>& tup, index<Is...>)
    {
       fprintf(file, convert(std::get<Is>(tup))...);
    }
private:
    std::tuple<Ts...> m_args;
};

class Logger
{
public:
    Logger();
    ~Logger();

    bool Start (const std::string& fileName)
    {
       m_file = std::fopen(fileName.c_str(), "a");
       if (!m_file)
       {
           return false;
       }

       m_run.store(true);
       m_thrd = std::move(std::thread([this]{ Run(); }));
       return true;
    }

    void Stop ()
    {
        m_run.store(false);
    }

    template<typename... Args>
    inline void Log (Args&&... args)
    {
        m_queue.push(CreateTransaction(std::forward<Args>(args)...));
    }

private:
    void Run()
    {
        while(m_run)
        {
            std::list<LogTransaction::Ptr> transactions = m_queue.pop_all();
            for (const auto& pTransaction : transactions)
            {
                pTransaction->Log(m_file);

                fprintf(file, "\n");
                fflush(file);
            }
        }

        std::fclose(m_file);
    }

    template<typename... Args>
    inline LogTransaction::Ptr CreateTransaction (Args&&... args)
    {
        LogStringTransaction<Args...>* pLogTransaction = new LogStringTransaction<Args...>(std::forward<Args>(args)...);
        return std::move(LogTransaction::Ptr(pLogTransaction));
    }

private:
    std::thread m_thrd;

    std::FILE* m_file;
    std::atomic_bool m_run;
    lockfree_queue<LogTransaction::Ptr> m_queue;
};

用法:

Logger logger();
Logger.start("test.log");

Logger.Log(....);

记录器不断崩溃。这是转储:

#2  0x0000000000497c63 in Log<char const (&) [57], std::basic_string<char, std::char_traits<char>, std::allocator<char> >&, std::basic_string<char, std::char_traits<char>, std::allocator<char> > const&, 0ul, 1ul, 2ul> (tup=..., file=0x1d00400, this=<optimized out>) at ./algo/orderd/../algoutil/include/algoutil/logger.hpp:122
#3  Log<char const (&) [57], std::basic_string<char, std::char_traits<char>, std::allocator<char> >&, std::basic_string<char, std::char_traits<char>, std::allocator<char> > const&> (tup=..., file=0x1d00400, this=<optimized out>) at ./algo/orderd/../algoutil/include/algoutil/logger.hpp:116
#4  tt::algoutil::LogStringTransaction<char const (&) [57], std::string&, std::string const&>::Log (this=<optimized out>, file=0x1d00400)
at ./algo/orderd/../algoutil/include/algoutil/logger.hpp:95 

根据转储。 std :: tuple保存对某些参数的引用,因为arg是lvalue。当参数超出范围时,Logger将崩溃,因为它使用垃圾对象。

如何制作所有元组项的副本?

1 个答案:

答案 0 :(得分:2)

上面的代码无法编译:类占用Ts...,但元组使用Args...。请将来发布MCVEs。

您可能正在以tuple<Args...>的转发参考方式存储Args... Args&&...。在这种情况下,Args...的类型包可以包含引用类型。

要解决此问题,请将您的元组类型更改为std::tuple<std::decay_t<Args>...>,这使得类型适合存储。

请注意,如果传递生命周期不足的原始字符数组,则仍然会失败。