10/09/2023 22:17
Post №244
[Delete]
struct FunctionCall;
/**
* Name of a variable.
*/
using Identifier = std::string;
/**
* Describes expression.
*/
using Expression = std::variant<glm::dvec3, double, Identifier, FunctionCall>;
/**
* Describes a function call.
*/
struct FunctionCall {
Identifier name;
std::vector<Expression> arguments;
};
10/09/2023 22:18
Post №245
[Delete]
Expression - это вектор, скаляр, имя переменной или вызов функции. А FunctionCall (вызов функции) содержит вектор из Expression. Если бы в FunctionCall был не вектор, а чистый Expression, то такой код было бы нельзя скомпилировать, потому что из-за циклической зависимости, нельзя определить размеры типов.
Далее, используются конструкции типа:
if (const glm::dvec3 *value = std::get_if<glm::dvec3>(&expression)) {
} else if (const double *value = std::get_if<double>(&expression)) {
} else if (const Identifier *identifier = std::get_if<Identifier>(&expression)) {
} else if (const FunctionCall *functionCall = std::get_if<FunctionCall>(&expression)) {
} else {
throw std::runtime_error("Unknown variant");
}
10/09/2023 22:21
Post №246
[Delete]
Считаю, что это удобнее наследования в данном случае.
Наследование предпочел бы, когда список операций с типом можно предсказать заранее.
10/09/2023 22:26
Post №247
[Delete]
Когда создавал типы для дерева данных, предпочел использовать наследование:
/**
* Tree-like data to be rendered with a template.
*/
class TemplateItem {
public:
using iterator = std::vector<std::shared_ptr<TemplateItem>>::iterator;
using const_iterator =
std::vector<std::shared_ptr<TemplateItem>>::const_iterator;
public:
virtual ~TemplateItem() = default;
/**
* @return child item by it's name (if the item has a map of children).
*/
virtual const TemplateItem &child(const std::string &name) const = 0;
/**
* @return string value for the item (if the item has such value).
*/
virtual std::string value() const = 0;
/**
* @return iterator for array of childred (if the item has array of children).
*/
virtual iterator begin() = 0;
/**
* @return iterator for array of childred (if the item has array of children).
*/
virtual iterator end() = 0;
/**
* @return iterator for array of childred (if the item has array of children).
*/
virtual const_iterator begin() const = 0;
/**
* @return iterator for array of childred (if the item has array of children).
*/
virtual const_iterator end() const = 0;
};
10/09/2023 22:27
Post №248
[Delete]
/**
* Empty template item. No value, no children.
*/
class Empty : public TemplateItem {
public:
const TemplateItem &child(const std::string &) const;
std::string value() const;
iterator begin();
iterator end();
const_iterator begin() const;
const_iterator end() const;
};
/**
* String template item. It has a value and doesn't have children.
*/
class String : public TemplateItem {
public:
String(const std::string &v);
const TemplateItem &child(const std::string &) const;
std::string value() const;
iterator begin();
iterator end();
const_iterator begin() const;
const_iterator end() const;
private:
std::string v;
};
10/09/2023 22:28
Post №249
[Delete]
/**
* Helper function to access a nested child by path.
*/
const TemplateItem &getDeepChild(const TemplateItem &root,
const std::vector<std::string> &path);
/**
* Object template item. It doesn't have a value and has children accessible by
* names.
*/
class Object : public TemplateItem {
public:
using InitializerList = std::initializer_list<
std::pair<const std::string, std::shared_ptr<TemplateItem>>>;
using Map = std::map<std::string, std::shared_ptr<TemplateItem>>;
public:
Object(InitializerList data);
Object(const Map &data);
Object(Map &&data);
const TemplateItem &child(const std::string &name) const;
std::string value() const;
iterator begin();
iterator end();
const_iterator begin() const;
const_iterator end() const;
private:
std::map<std::string, std::shared_ptr<TemplateItem>> data;
};
10/09/2023 22:29
Post №250
[Delete]
/**
* Array template item. It doesn't have a value and has childern accessible as
* an array.
*/
class Array : public TemplateItem {
public:
using InitializerList = std::initializer_list<std::shared_ptr<TemplateItem>>;
using Vector = std::vector<std::shared_ptr<TemplateItem>>;
public:
Array(InitializerList data);
Array(const Vector &data);
Array(Vector &&data);
const TemplateItem &child(const std::string &name) const;
std::string value() const;
iterator begin();
iterator end();
const_iterator begin() const;
const_iterator end() const;
private:
Vector data;
};
10/09/2023 22:30
Post №251
[Delete]
Когда использую наследование, почти всегда прихожу к необходимости использовать std::shared_ptr.
11/09/2023 18:23
Post №254
[Delete]
>>245
>Expression - это вектор, скаляр, имя переменной или вызов функции. А FunctionCall (вызов функции) содержит вектор из Expression.
Хорошо бы пояснить на примере конкретной задачи, где такое может оказаться полезным.
11/09/2023 23:19
Post №258
[Delete]
>>254
>Хорошо бы пояснить на примере конкретной задачи, где такое может оказаться полезным.
На кислице был мой тред про софтовый рендеринг. Сделал такой формат описания моделей:
{
"dots": {
"house.a0": [-30, 0, -10],
"house.b0": [-20, 0, -10],
"house.c0": [-20, 0, -16],
"house.a1": [-30, 3, -10],
"house.b1": [-20, 3, -10],
"house.c1": [-20, 3, -16],
...
},
"lines": [
["house.a0", "house.b0", "house.c0"],
["house.a1", "house.b1", "house.c1"],
["house.a0", "house.a1"],
...
]
}
Тут рисуются контуры 3 стен дома. Неудобно. Чтобы его повернуть или передвинуть, надо менять координаты всех точек. А там еще есть окна и дверь.
Хочу, чтобы можно было задать 1 точку и 3 вектора, а остальное описать формулами:
{
"vars": {
"house.base": [-30, 0, -10],
"house.size_a": [10, 0, 0],
"house.size_b": [0, 0, -6],
"house.size_c": [0, 3, 0],
"house.dot.a": ["sum", "house.base", "house.size_a"],
"house.dot.b": ["sum", "house.base", "house.size_b"],
"house.dot.c": ["sum", "house.base", "house.size_c"],
...
"window.dot.a": ["sum", "house.base", ["mul", "house.size_a", 0.25], ["mul", "house.size_c", 0.25]],
...
},
...
}