В приведенном ниже коде при использовании std::shared_ptr я получаю ошибку сегментации. Однако при использовании обычных указателей та же проблема не возникает.
Подводя итог, я хочу определить объект ConstrainedVariable, который будет обновляться в соответствии с некоторой кривой всякий раз, когда обновляется значение ссылочной переменной. Способ, который я нашел для реализации этого, включает использование интеллектуальных указателей, поскольку они автоматически удаляются из содержащих их векторов, когда на них больше не ссылаются (например, когда переменная больше не используется).
#include <vector>
#include <functional>
#include <memory>
#include <iostream>
#include <cmath>
using Action = std::function<void()>;
using ActionPtr = std::shared_ptr<Action>;
using ActionWeakPtr = std::weak_ptr<Action>;
using VectAct = std::vector<ActionWeakPtr>;
inline void execute(ActionPtr t){(*t)();}
inline ActionPtr action(Action t) {return std::make_shared<Action>(t);}
template<class T>
class Variable {
protected:
T ival; // internal value
std::vector<ActionWeakPtr> actions;
public:
Variable() = default;
inline void link(ActionPtr t) {actions.push_back(t);}
void set(T newval) {
if (ival == newval) return;
static_set(newval);
force_update();
}
T val() {return ival;}
private:
void static_set(T newval){
ival = newval;
}
void force_update(){
for(auto it = actions.begin(); it != actions.end(); /* Nothing */){
auto action = it->lock();
if (action){execute(action); it++;}
else {it = actions.erase(it);}
}
}
};
template<class T>
using VariablePtr = std::shared_ptr<Variable<T>>;
template<class T>
inline VariablePtr<T> var() {return std::make_shared<Variable<T>>();};
template<class I, class O>
using Curve = std::function<O(I)>;
template<class I, class O>
class ConstrainedVariable : private Variable<O> {
ActionPtr constraint;
std::weak_ptr<Variable<I>> _refVar;
Curve<I, O> _refCurve = [](I t){return std::sin(t);};
public:
void applyConstraint() {
if (auto sharedVar = _refVar.lock()){
this->set(_refCurve(sharedVar->val()));
}
}
O val() {return this->ival;}
ConstrainedVariable(VariablePtr<I> refVar, Curve<I, O> refCurve) :
Variable<O>(), _refVar(refVar), _refCurve(refCurve) {
constraint = action([this](){this->applyConstraint();});
if (auto v = _refVar.lock()){
v->link(constraint);
}
execute(constraint);
}
};
template<class I, class O>
std::shared_ptr<ConstrainedVariable<I, O>> ctrVar(VariablePtr<I> refVar, Curve<I, O> refCurve){
return std::make_shared<ConstrainedVariable<I, O>>(ConstrainedVariable<I, O>(refVar, refCurve));
};
int main(){
auto v = var<float>();
//auto _c = ConstrainedVariable<float, float>(v, [](float t){return std::sin(t);});
//auto* c = &_c; // WORKS
auto c = ctrVar<float, float>(v, [](float t){return std::sin(t);}); // DOESNT WORK
std::cout << v->val() << std::endl;
std::cout << c->val() << std::endl;
v->set(1);
std::cout << v->val() << std::endl;
std::cout << c->val() << std::endl;
}
Мне не удалось минимально воспроизвести проблему и я не понимаю, почему это происходит. Любая помощь приветствуется.
Из GDB: #0 0x000000000060c050 в ?? () #1 0x0000000000402c70 в std::function<float (float)>::operator()(float) const (this=0x7fffffffd970, __args#0=-nan(0x7fd930)) в /opt/rh/devtoolset-8/ root/usr/include/c++/8/bits/std_function.h:687 #2 0x0000000000402592 в ConstrainedVariable<float, float>::applyConstraint (this=0x7ffffffd930) в temp.cpp:67 при переходе к кадру №2 и просмотре при возврате _refVal->val() значением является -nan. Таким образом, ошибка сегмента происходит при вызове _refCurve для этого значения nan.
Проблема в ctrVar
. Вы создаете временный ConstrainedVariable
в параметрах make_shared
, который затем копируется в указатель.
Поскольку действие constraint
инициализируется с помощью захвата лямбды this
, оно зависает, когда оригинал перестает существовать. ConstrainedVariable
копировать или перемещать небезопасно, поэтому вам следует = delete
использовать эти специальные функции-члены или исправить их, чтобы они регистрировались при копировании или перемещении.
Непосредственное решение — передать параметры конструктора make_shared
, чтобы вы не копировали ConstrainedVariable
в свой образец.
template<class I, class O>
std::shared_ptr<ConstrainedVariable<I, O>> ctrVar(VariablePtr<I> refVar, Curve<I, O> refCurve){
return std::make_shared<ConstrainedVariable<I, O>>(refVar, refCurve);
};
Спасибо! Именно в этом и была проблема! Еще один вопрос для моего личностного роста: плох ли «беспорядок указателей»? Я не могу избавиться от ощущения, что я делаю что-то не так в своем дизайне.
Ничего особенного, просто у меня были небольшие проблемы с поиском того, какой из них висит. Смотри редактирование, я только что нашел его
Если я правильно понял ваш вариант использования, возможно, вы захотите использовать std::atomicstd::shared_ptr в шаблоне ctrVar вместо простого Shared_ptr.
Ошибка сегментации на чем именно? Вы пробовали использовать отладчик, чтобы увидеть, в чем проблема?