本文主要是介绍【开发日志】2022.08.22 支持ZENO把primitive对象导出为csv文件【参考结点 ParamFormat 、ParamFileParser】,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!
任务:支持把primitive对象导出为csv文件,方便用户在excel里打开查看
预备知识
CSV
读取CSV
#include <iostream>
#include <fstream>
#include <vector>
#include <string>using namespace std;vector<vector<string>> readCSV(string path) {vector<vector<string>> data;// 打开CSV文件ifstream file(path);if (!file.is_open()) {cout << "无法打开文件:" << path << endl;return data;}// 逐行读取文件内容string line;while (getline(file, line)) {vector<string> row;string field;// 将每一行按照逗号分隔符进行分割,并存储到vector中for (char c : line) {if (c == ',') {row.push_back(field);field.clear();} else {field += c;}}row.push_back(field);data.push_back(row);}// 关闭文件file.close();return data;
}int main() {string path = "data.csv";vector<vector<string>> data = readCSV(path);for (auto& row : data) {for (auto& field : row) {cout << field << ",";}cout << endl;}return 0;
}
导出csv
#include <iostream>
#include <fstream>
#include <vector>
#include <string>using namespace std;void writeCSV(string path, vector<vector<string>> data) {// 创建CSV文件并打开文件ofstream file(path);if (!file.is_open()) {cout << "无法创建文件:" << path << endl;return;}// 将vector中的数据写入CSV文件中for (auto& row : data) {for (auto& field : row) {file << field << ",";}file << endl;}// 关闭文件file.close();
}int main() {string path = "data.csv";vector<vector<string>> data = {{"id", "name", "age"},{"1", "张三", "20"},{"2", "李四", "21"},{"3", "王五", "22"}};writeCSV(path, data);cout << "CSV文件已成功导出到:" << path << endl;return 0;
}
C++字符串转换(stoi;stol;stoul;stoll;stoull;stof;stod;stold)
\Yongqiang Cheng的博客-CSDN博客_std::stoi
C++字符串转换(stoi;stol;stoul;stoll;stoull;stof;stod;stold)_WilliamX2020的博客-CSDN博客_stoul
void testTypeConvert()
{//int --> stringint i = 5;string s = to_string(i);cout << s << endl;//double --> stringdouble d = 3.14;cout << to_string(d) << endl;//long --> stringlong l = 123234567;cout << to_string(l) << endl;//char --> stringchar c = 'a';cout << to_string(c) << endl; //自动转换成int类型的参数//char --> stringstring cStr; cStr += c;cout << cStr << endl;s = "123.257";//string --> int;cout << stoi(s) << endl;//string --> intcout << stol(s) << endl;//string --> floatcout << stof(s) << endl;//string --> doubelcout << stod(s) << endl;
}
output:
53.14000012323456797a123123123.257123.257
std::varient
从C中采用的c++提供了对union的支持,union是能够保存可能类型列表之一的对象。但是,这种语言特性也有一些缺点:
对象不知道它们当前持有的值的类型。
由于这个原因,您不能有non-trivial的成员,比如std::string(从c++ 11起, union原则上可以有non-trivial的成员,但是必须实现特殊的成员函数,比如复制构造函数和析构函数,因为只有通过代码逻辑才能知道哪个成员是可用的。)
不能从union中派生类。
对于std:: variable <>, c++标准库提供了一个封闭的区分联合(这意味着有一个指定的可能类型列表,可以指定你要指的是哪种类型),其中:
当前值的类型总是已知的;
可以有任何指定类型的成员;
可以派生类。
事实上,一个std:: variable <>拥有不同的替代值,这些替代值通常具有不同的类型。与std::optional<>和std::any一样,生成的任何对象都具有值语义。也就是说,通过在它自己的内存中创建一个具有当前替代的当前值的独立对象来进行深度复制。因此,复制std:: variable <>与复制当前替代的类型/值一样便宜/昂贵。支持Move语义。
std:: variable <>的核心功能:
#include <variant>std::variant<int, std::string> var{"hi"}; // initialized with string alternative
std::cout << var.index(); // prints 1
var = 42; // now holds int alternative
std::cout << var.index(); // prints 0
...
try {
std::string s = std::get<std::string>(var); // access by type
int i = std::get<0>(var); // access by index
}
catch (const std::bad_variant_access& e) { // in case a wrong type/index is used
...
}
成员函数index()可用于查明当前设置了哪个选项(第一个选项的索引为0)。初始化和赋值总是使用最佳匹配来找到新选项。如果类型不完全匹配,可能会出现意外。
注意,不允许使用空变量、具有引用成员的变量、具有c样式数组成员的变量和具有不完整类型(如void)的变量。没有空的状态:这意味着对于每个构建的对象,必须至少调用一个构造函数。默认构造函数初始化第一个类型(通过第一个类型的默认构造函数):
std::variant<std::string, int> var; // => var.index() == 0, value == ””
如果没有为第一个类型定义默认构造函数,则调用该变量的默认构造函数会在编译时错误
不过辅助类型std::monostate提供了处理这种情况的能力,还提供了模拟空状态的能力。
std::monostate
为了支持第一个类型没有默认构造函数的variant对象,提供了一个特殊的helper类型:std::monostate。类型std::monostate的对象总是具有相同的状态,因此,它们总是相等的。它自己的目的是表示另一种类型,这样variant就没有任何其他类型的值。也就是说,std::monostate可以作为第一种替代类型,使变体类型默认为可构造的。例如:
std::variant<std::monostate, NoDefConstr> v2; // OK
std::cout << "index: " << v2.index() << '\n'; // prints 0
在某种程度上,你可以把这种状态解释为模拟的(原则上,std::monostate可以作为任何替代,而不仅仅是第一个替代,当然,这个替代不能帮助使变体的默认构造成为可构造的)。
有多种方法可以检查monostate,这也演示了一些其他的操作,你可以调用变量:
#include <iostream>
#include <variant>struct NoDefConstr
{NoDefConstr(int i){std::cout << "NoDefConstr::NoDefConstr(int) called\n";}
};int main()
{std::variant<std::monostate, NoDefConstr> v2; // OKstd::cout << "index: " << v2.index() << '\n'; // prints 0if (v2.index() == 0) {std::cout << "has monostate\n";}if (!v2.index()) {std::cout << "has monostate\n";}if (std::holds_alternative<std::monostate>(v2)) {std::cout << "has monostate\n";}if (std::get_if<0>(&v2)) {std::cout << "has monostate\n";}if (std::get_if<std::monostate>(&v2)) {std::cout << "has monostate\n";}return 0;
}
output:
get_if < T>()使用一个指向一个variant的指针,如果当前的选项是T,返回一个指向当前选项的指针,否则它将返回nullptr。这与get<T>()不同,get<T>()接受对变量的引用,如果提供的类型正确,则按值返回当前替代,否则抛出异常。和往常一样,您可以为另一个选项赋值,甚至可以为monostate赋值,再次表示为空:
v2 = 42;
std::cout << "index: " << v2.index() << '\n'; // index: 1v2 = std::monostate{};
std::cout << "index: " << v2.index() << '\n'; // index: 0
variant的派生类
可以从std::variant派生出子类。例如,可以定义一个从std:: variable <>派生的聚合,如下所示:
include <iostream>
#include <variant>class Derived : public std::variant<int, std::string>
{
};int main()
{Derived d = { {"hello"} };std::cout << d.index() << '\n'; // prints: 1std::cout << std::get<1>(d) << '\n'; // prints: hellod.emplace<0>(77); // initializes int, destroys stringstd::cout << d.index() << '\n'; // prints: 0std::cout << std::get<0>(d) << '\n'; // prints: 77return 0;
}
output:
std::variant<>的类型
在头文件< variable >中,c++标准库定义了类std:: variable <>,如下所示:
namespace std
{template<typename Types...> class variant;
}
也就是说,std:: variable <>是一个可变参数类模板(c++ 11引入的一个特性,允许处理任意数量的类型)。
此外,定义了以下类型和对象:
类型 std::variant_size
类型 std::variant_alternative
值 std::variant_npos
类型 std::monostate
异常类std::bad_variant_access派生自std:: Exception。
std::variant操作
如下列出了为std:: variable <>提供的所有操作。
1. 构造函数
默认情况下,变量的默认构造函数调用第一个备选项的默认构造函数:
std::variant<int, int, std::string> v1; // sets first int to 0, index()==0
另一种方法是初始化值,这意味着对于基本类型,它是0、false还是nullptr。如果传递一个值进行初始化,则使用最佳匹配类型:
std::variant<long, int> v2{42};
std::cout << v2.index() << '\n'; // prints 1
然而,如果两种类型匹配得同样,则调用是不明确的:
std::variant<long, long> v3{42}; // ERROR: ambiguous
std::variant<int, float> v4{42.3}; // ERROR: ambiguous
std::variant<int, double> v5{42.3}; // OK
std::variant<std::string, std::string_view> v6{"hello"}; // ERROR: ambiguous
std::variant<std::string, std::string_view, const char*> v7{"hello"}; // OK
std::cout << v7.index() << '\n'; // prints 2
要传递多个值进行初始化,必须使用in_place_type或in_place_index标记:
std::variant<std::complex<double>> v8{3.0, 4.0}; // ERROR
std::variant<std::complex<double>> v9{{3.0, 4.0}}; // ERROR
std::variant<std::complex<double>> v10{std::in_place_type<std::complex<double>>,
3.0, 4.0};
std::variant<std::complex<double>> v11{std::in_place_index<0>, 3.0, 4.0};
当然,可以使用in_place_index标签来解决初始化过程中的歧义或匹配问题:
std::variant<int, int> v12{std::in_place_index<1>, 77}; // init 2nd int
std::variant<int, long> v13{std::in_place_index<1>, 77}; // init long, not int
std::cout << v13.index() << '\n'; // prints 1
甚至可以传递一个初始化器列表,后面跟着附加的参数:
// initialize variant with a set with lambda as sorting criterion:
auto sc = [] (int x, int y)
{return std::abs(x) < std::abs(y);
};std::variant<std::vector<int>,std::set<int,decltype(sc)>> v14{std::in_place_index<1>, {4, 8, -7, -2, 0, 5}, sc};
不能对std:: variable <>使用类模板参数推导,而且不存在make_variable <>()便利函数(与std::optional<>和std::any不同)。两者都没有意义,因为变体的整个目标是处理多个替代方案。
访问值
访问值的通常方法是调用get<>()获取对应的选项值。可以传递它的索引或者类型。例如:
std::variant<int, int, std::string> var; // sets first int to 0, index()==0
auto a = std::get<double>(var); // compile-time ERROR: no double
auto b = std::get<4>(var); // compile-time ERROR: no 4th alternative
auto c = std::get<int>(var); // compile-time ERROR: int twicetry{auto s = std::get<std::string>(var); // throws exception (first int currently set)auto i = std::get<0>(var); // OK, i==0auto j = std::get<1>(var); // throws exception (other int currently set)
}
catch (const std::bad_variant_access& e) { // in case of an invalid accessstd::cout << "Exception: " << e.what() << '\n';
}
也有一个API来访问该值的选项,检查它是否存在:
if (auto ip = std::get_if<1>(&var); ip)
{std::cout << *ip << '\n';
}
else
{std::cout << "alternative with index 1 not set\n";
}
必须将variant变量的指针传递给get_if<>(),它要么返回指向当前值的指针,要么返回nullptr。注意,这里使用了if with initialize,它允许检查刚刚初始化的值。另一种访问不同选项值的方法是variant访问器(后续文章会介绍)。
修改值
赋值和emplace()操作对应于初始化:
std::variant<int, int, std::string> var; // sets first int to 0, index()==0
var = "hello"; // sets string, index()==2
var.emplace<1>(42); // sets second int, index()==1
还可以使用get<>()或get_if<>()来为当前选项值分配一个新值:
std::variant<int, int, std::string> var; // sets first int to 0, index()==0
std::get<0>(var) = 77; // OK, because first int already set
std::get<1>(var) = 99; // throws exception (other int currently set)if (auto p = std::get_if<1>(&var); p) { // if second int set
*p = 42; // modify it
}
修改不同备选项值的另一种方法是使用不同的访问者。
4. variant对象比较
对于两个类型相同的variant(相同的备选项和顺序),可以使用通常的比较运算符。运算符根据如下规则:
对于都有值的两个variant对象,index小的对象大于index大的对象;
对于两个都有值并且index也相等的两个variant对象,按照其相应类型的比较运算符比较。注意所有对象的std::monostate对象总是相等的;
例4:
#include <iostream>
#include <variant>int main()
{std::variant<std::monostate, int, std::string, float> v1, v2{ "hello" }, v3{ 42 };std::variant<std::monostate, std::string, int> v4;//v1 == v4 // COMPILE-TIME ERRORstd::cout << std::boolalpha;std::cout << (v1 == v2) << std::endl;// yields falsestd::cout << (v1 < v2) << std::endl; // yields truestd::cout << (v1 < v3) << std::endl; // yields truestd::cout << (v2 < v3) << std::endl; // yields falsestd::cout << (v2 == v3) << std::endl; // yields falsestd::cout << (v2 > v3) << std::endl; // yields truev1 = "hello";std::cout << (v1 == v2) << std::endl;// yields truev2 = 41;std::cout << (v2 < v3) << std::endl; // yields truestd::cout << std::noboolalpha;return 0;
}
参考结点
ParamFormat 、ParamFileParser
InputParams.cpp
#include <zeno/zeno.h>
#include <zeno/types/DictObject.h>
#include <zeno/types/NumericObject.h>
#include <filesystem>
#include "zeno/utils/log.h"
#include "zeno/types/ListObject.h"
#include "zeno/utils/string.h"
#include <cstdio>
#include <fstream>
#include <set>namespace zeno {
namespace {using DefaultValue = std::variant<int,vec2i,vec3i,vec4i,float,vec2f,vec3f,vec4f,std::string>;struct ParamFormatInfo: IObject {std::string name;std::string _type;DefaultValue defaultValue;
};struct ParamFormat : zeno::INode {virtual void apply() override {auto format = std::make_shared<zeno::ParamFormatInfo>();format->name = get_input2<std::string>("name");format->_type = get_input2<std::string>("type");auto defaultValue = get_input2<std::string>("defaultValue");auto items = zeno::split_str(defaultValue, ',');if (format->_type == "int") {format->defaultValue = std::stoi(defaultValue);}else if (format->_type == "vec2i") {format->defaultValue = vec2i(std::stoi(items[0]),std::stoi(items[1]));}else if (format->_type == "vec3i") {format->defaultValue = vec3i(std::stoi(items[0]),std::stoi(items[1]),std::stoi(items[2]));}else if (format->_type == "vec4i") {format->defaultValue = vec4i(std::stoi(items[0]),std::stoi(items[1]),std::stoi(items[2]),std::stoi(items[3]));}else if (format->_type == "float") {format->defaultValue = std::stof(defaultValue);}else if (format->_type == "vec2f") {format->defaultValue = vec2f(std::stof(items[0]),std::stof(items[1]));}else if (format->_type == "vec3f") {format->defaultValue = vec3f(std::stof(items[0]),std::stof(items[1]),std::stof(items[2]));}else if (format->_type == "vec4f") {format->defaultValue = vec4f(std::stof(items[0]),std::stof(items[1]),std::stof(items[2]),std::stof(items[3]));}else {format->defaultValue = defaultValue;}set_output("format", std::move(format));}
};ZENDEFNODE(ParamFormat, {{{"string", "name"},{"enum float vec2f vec3f vec4f int vec2i vec3i vec4i string", "type", "string"},{"string", "defaultValue"},},{"format"},{},{"string"},
});struct ParamFileParser : zeno::INode {virtual void apply() override {auto formatList = get_input<zeno::ListObject>("formatList");auto params = std::make_shared<zeno::DictObject>();auto path = get_input2<std::string>("configFilePath");std::set<std::string> saved_names;if (std::filesystem::exists(path)) {auto is = std::ifstream(path);while (!is.eof()) {std::string line;std::getline(is, line);line = zeno::trim_string(line);if (line.empty()) {continue;}auto items = zeno::split_str(line, ',');zany value;if (items[1] == "int") {value = std::make_shared<NumericObject>(std::stoi(items[2]));}else if (items[1] == "vec2i") {value = std::make_shared<NumericObject>(vec2i(std::stoi(items[2]),std::stoi(items[3])));}else if (items[1] == "vec3i") {value = std::make_shared<NumericObject>(vec3i(std::stoi(items[2]),std::stoi(items[3]),std::stoi(items[4])));}else if (items[1] == "vec4i") {value = std::make_shared<NumericObject>(vec4i(std::stoi(items[2]),std::stoi(items[3]),std::stoi(items[4]),std::stoi(items[5])));}else if (items[1] == "float") {value = std::make_shared<NumericObject>(std::stof(items[2]));}else if (items[1] == "vec2f") {value = std::make_shared<NumericObject>(vec2f(std::stof(items[2]),std::stof(items[3])));}else if (items[1] == "vec3f") {value = std::make_shared<NumericObject>(vec3f(std::stof(items[2]),std::stof(items[3]),std::stof(items[4])));}else if (items[1] == "vec4f") {value = std::make_shared<NumericObject>(vec4f(std::stof(items[2]),std::stof(items[3]),std::stof(items[4]),std::stof(items[5])));}else {value = std::make_shared<StringObject>(items[2]);}params->lut[items[0]] = value;saved_names.insert(items[0]);}is.close();}{FILE *fp = fopen(path.c_str(), "a");if (!fp) {perror(path.c_str());abort();}for (auto &ptr: formatList->arr) {auto p = std::static_pointer_cast<ParamFormatInfo>(ptr);if (saved_names.count(p->name)) {continue;}zany value;if (std::holds_alternative<int>(p->defaultValue)) {auto v = std::get<int>(p->defaultValue);value = std::make_shared<NumericObject>(v);fprintf(fp, "%s,%s,%d\n", p->name.c_str(), p->_type.c_str(), v);}else if (std::holds_alternative<vec2i>(p->defaultValue)) {auto v = std::get<vec2i>(p->defaultValue);value = std::make_shared<NumericObject>(v);fprintf(fp, "%s,%s,%d,%d\n", p->name.c_str(), p->_type.c_str(), v[0], v[1]);}else if (std::holds_alternative<vec3i>(p->defaultValue)) {auto v = std::get<vec3i>(p->defaultValue);value = std::make_shared<NumericObject>(v);fprintf(fp, "%s,%s,%d,%d,%d\n", p->name.c_str(), p->_type.c_str(), v[0], v[1], v[2]);}else if (std::holds_alternative<vec4i>(p->defaultValue)) {auto v = std::get<vec4i>(p->defaultValue);value = std::make_shared<NumericObject>(v);fprintf(fp, "%s,%s,%d,%d,%d,%d\n", p->name.c_str(), p->_type.c_str(), v[0], v[1], v[2], v[3]);}else if (std::holds_alternative<float>(p->defaultValue)) {auto v = std::get<float>(p->defaultValue);value = std::make_shared<NumericObject>(v);fprintf(fp, "%s,%s,%f\n", p->name.c_str(), p->_type.c_str(), v);}else if (std::holds_alternative<vec2f>(p->defaultValue)) {auto v = std::get<vec2f>(p->defaultValue);value = std::make_shared<NumericObject>(v);fprintf(fp, "%s,%s,%f,%f\n", p->name.c_str(), p->_type.c_str(), v[0], v[1]);}else if (std::holds_alternative<vec3f>(p->defaultValue)) {auto v = std::get<vec3f>(p->defaultValue);value = std::make_shared<NumericObject>(v);fprintf(fp, "%s,%s,%f,%f,%f\n", p->name.c_str(), p->_type.c_str(), v[0], v[1], v[2]);}else if (std::holds_alternative<vec4f>(p->defaultValue)) {auto v = std::get<vec4f>(p->defaultValue);value = std::make_shared<NumericObject>(v);fprintf(fp, "%s,%s,%f,%f,%f,%f\n", p->name.c_str(), p->_type.c_str(), v[0], v[1], v[2], v[3]);}else if (std::holds_alternative<std::string>(p->defaultValue)) {auto v = std::get<std::string>(p->defaultValue);value = std::make_shared<StringObject>(v);fprintf(fp, "%s,%s,%s\n", p->name.c_str(), p->_type.c_str(), v.c_str());}params->lut[p->name] = value;}fclose(fp);}set_output("params", std::move(params));}
};ZENDEFNODE(ParamFileParser, {{"formatList",{"writepath", "configFilePath"},},{{"DictObject", "params"},},{},{"string"},
});}
}
PrimCSVIO.cpp
#include <zeno/zeno.h>
#include <zeno/types/PrimitiveObject.h>
#include <zeno/funcs/PrimitiveUtils.h>
#include <zeno/types/StringObject.h>
#include <zeno/utils/variantswitch.h>
#include <zeno/utils/arrayindex.h>
#include <zeno/utils/string.h>
#include <zeno/utils/log.h>
#include <zeno/utils/vec.h>
#include <fstream>namespace zeno {
namespace {static void dump(int const &v, std::ostream &fout) {fout << v;
}static void dump(float const &v, std::ostream &fout) {fout << v;
}template <size_t N, class T>
static void dump(vec<N, T> const &v, std::ostream &fout) {fout << v[0];for (int i = 1; i < N; i++)fout << ' ' << v[i];
}template <class T>
void dump_csv(AttrVector<T> avec, std::ostream &fout) {fout << "pos";avec.template foreach_attr<AttrAcceptAll>([&] (auto const &key, auto &arr) {fout << ',' << key;});fout << '\n';for (int i = 0; i < avec.size(); i++) {dump(avec[i], fout);avec.template foreach_attr<AttrAcceptAll>([&] (auto const &key, auto &arr) {fout << ',';dump(arr[i], fout);});fout << '\n';}
}struct WritePrimToCSV : INode {virtual void apply() override {auto prim = get_input<PrimitiveObject>("prim");auto path = get_input<StringObject>("path")->get();std::ofstream fout(path);auto mbr = funcalt_variant(array_index({"verts", "points", "lines", "tris", "quads", "loops", "polys"},get_input2<std::string>("type")),&PrimitiveObject::verts,&PrimitiveObject::points,&PrimitiveObject::lines,&PrimitiveObject::tris,&PrimitiveObject::quads,&PrimitiveObject::loops,&PrimitiveObject::polys);std::visit([&] (auto const &mbr) {dump_csv(mbr(*prim), fout);}, mbr);set_output("prim", std::move(prim));}
};ZENDEFNODE(WritePrimToCSV,{ /* inputs: */ {{"primitive", "prim"},{"writepath", "path"},{"enum verts points lines tris quads loops polys", "type", "verts"},}, /* outputs: */ {{"primitive", "prim"},}, /* params: */ {}, /* category: */ {"primitive",}});}
}
这篇关于【开发日志】2022.08.22 支持ZENO把primitive对象导出为csv文件【参考结点 ParamFormat 、ParamFileParser】的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!