我正在实现一个将打印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将崩溃,因为它使用垃圾对象。
如何制作所有元组项的副本?
答案 0 :(得分:2)
上面的代码无法编译:类占用Ts...
,但元组使用Args...
。请将来发布MCVEs。
您可能正在以tuple<Args...>
的转发参考方式存储Args...
Args&&...
。在这种情况下,Args...
的类型包可以包含引用类型。
要解决此问题,请将您的元组类型更改为std::tuple<std::decay_t<Args>...>
,这使得类型适合存储。
请注意,如果传递生命周期不足的原始字符数组,则仍然会失败。