我想實作以下目標:
export_vars("path/to/file.dat", {"variable_name", obj}, {"another_variable", 2});
whereobj可以是任何型別,只要它有<<多載 - 這個想法是ofstream稍后寫入。我試過(一initializer_list對):
void
export_vars(const std::string& path, std::initializer_list<std::pair<std::string, std::any>> args)
{
for (auto& [name, var] : args)
std::cout << name << ": " << var << std::endl;
}
但std::any不能不<<知道底層型別。可以使用可變引數模板和引數包擴展來實作嗎?我也嘗試過類似的事情:
template <class... Args>
void
export_vars(const std::string& path, Args... args)
{
(std::cout << ... << args.first << args.second) << std::endl;
}
但這顯然是錯誤的。有什么建議?
uj5u.com熱心網友回復:
{..} 沒有型別,因此不允許大多數推導。
幾種解決方法:
將呼叫更改為
std::pair顯式使用:template <typename ... Pairs> void export_vars(const std::string&, const Pairs&... args) { ((std::cout << args.first << ": " << args.second << std::endl), ...); } int main() { export_vars("unused", std::pair{"int", 42}, std::pair{"cstring", "toto"}); }演示
不要使用模板:
void export_vars(const std::string&, const std::initializer_list<std::pair<std::string, Streamable>>& args) { for (const auto& [name, value] : args) { std::cout << name << ": " << value << std::endl; } } int main() { export_vars("unused", {{"int", 42}, {"cstring", "toto"}}); }與
Streamable使用型別擦除,可能是這樣的:class Streamable { struct IStreamable { virtual ~IStreamable() = default; virtual void print(std::ostream&) = 0; }; template <typename T> struct StreamableT : IStreamable { StreamableT(T t) : data(std::forward<T>(t)) {} virtual void print(std::ostream& os) { os << data; } T data; }; std::unique_ptr<IStreamable> ptr; public: template <typename T> // Possibly some concepts/SFINAE as requires(is_streamable<T>) Streamable(T&& t) : ptr{std::make_unique<StreamableT<std::decay_t<T>>>(t)} {} friend std::ostream& operator << (std::ostream& os, const Streamable& streamable) { streamable.ptr->print(os); return os; } };演示
uj5u.com熱心網友回復:
模板化的遞回函式可以解決這個問題。
遞回函式作為引數:
- 需要通過所有層的物件,在這種情況下是一個輸出流參考
- 后跟您希望一次處理一個的物件,在本例中是一個字串和一個模板化物件
- 最后是可變引數包,它捕獲所有剩余的引數。
遞回函式僅處理單個給定對,然后在可變引數上遞回呼叫自身。最后需要一個簡單的函式來結束遞回。
在這種情況下,使用輸出流參考更容易,因為它可以遞回傳遞。您需要在另一個函式中處理打開檔案等。
一個例子:
#include <string>
#include <iostream>
#include <utility>
void export_vars(std::ostream& o)
{
}
template<typename T, typename... Args>
void export_vars(std::ostream& o, const std::string& name, const T& var, Args&&... args)
{
o << name << ": " << var << std::endl;
export_vars(o, std::forward<Args>(args)...);
}
int main()
{
export_vars(std::cout, "test", int(0), "test2", unsigned(1));
}
演示:https : //godbolt.org/z/v9Gv9MG5d
在這種情況下,我選擇簡單地將名稱和變數作為單獨的物件放置,因為這實際上需要最少的語法使用。
當然也可以使用對:
template<typename T, typename... Args>
void export_vars(std::ostream& o, const std::pair<std::string,T>& var, Args&&... args)
{
o << var.first << ": " << var.second << std::endl;
export_vars(o, std::forward<Args>(args)...);
}
但是,您不能使用所需的{"str",var}語法,因為編譯器不知道它應該轉換為哪種型別。但是std::make_pair("str",var)還是std::pair{"str",var}應該作業。
uj5u.com熱心網友回復:
在std::any 檔案的幫助下,我想出了這個解決方案。它并不完美,因為您需要手動為每種型別注冊列印函式(訪問者),但至少您可以使用export_vars帶有 <string, any> 對的容器,并且沒有遞回模板。
演示
#include <type_traits>
#include <any>
#include <functional>
#include <iomanip>
#include <iostream>
#include <typeindex>
#include <typeinfo>
#include <unordered_map>
#include <vector>
template <class T, class F>
inline std::pair<const std::type_index, std::function<void(std::ostream& ostr, std::any const&)>> to_any_visitor(F const& f)
{
return { std::type_index(typeid(T)), [g = f](std::ostream& ostr, std::any const& a) {
if constexpr (std::is_void_v<T>)
g(ostr);
else
g(ostr, std::any_cast<T const&>(a));
} };
}
static std::unordered_map<std::type_index, std::function<void(std::ostream& ostr, std::any const&)>> any_visitor{
to_any_visitor<void>([](std::ostream& ostr) { ostr << "{}"; }),
to_any_visitor<int>([](std::ostream& ostr, int x) { ostr << x; }),
to_any_visitor<unsigned>([](std::ostream& ostr, unsigned x) { ostr << x; }),
to_any_visitor<float>([](std::ostream& ostr, float x) { ostr << x; }),
to_any_visitor<double>([](std::ostream& ostr, double x) { ostr << x; }),
to_any_visitor<char const*>([](std::ostream& ostr, char const* s) { ostr << std::quoted(s); })
};
void export_vars(std::ostream& ostr, const std::vector<std::pair<std::string, std::any>>& args)
{
for (const auto& [name, var] : args)
{
if (const auto it = any_visitor.find(std::type_index(var.type())); it != any_visitor.cend())
{
ostr << name << ": ";
it->second(ostr, var);
ostr << std::endl;
}
else
{
throw std::runtime_error("Print function not registered");
}
}
}
int main()
{
std::vector<std::pair<std::string, std::any>> pairs{ { "xxx", 123.456 }, { "yyy", "some text" }, { "zzz", 789 } };
export_vars(std::cout, pairs);
export_vars(std::cout, {{"xxx", 123}, {"yyy", 5.6}}); // this will also work
}
轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/370015.html
上一篇:模板引數推導在C 14上失敗
