I cannot believe my eyes I’ve meet this problem that seems to be impossible according to documentation. std::make_shared<USER_TYRE> initializes user type differently from the std::shared_ptr(new USER_TYRE), where USER_TYRE is a child class to an abstract one.
auto p_obj = std::make_shared<USER_TYRE>(1);
std::shared_ptr<USER_TYRE> p_obj (new USER_TYRE(1));
auto p_obj = USER_TYRE(1);
USER_TYRE publically iherit abstract class.
And the first case initialise the USER_TYRE differently from two other and incur the application crash. First case demonstrate non anticipated behavior unlike the others. Before abstract class made there were no problems.
To explain this I should say all background and details how I met this problem.
I had been using the Qt Creator 13.0.0 (Community) IDE to create GUI application kind of calculator.
Firstly I had written a working instance. First working instance correctly worked with the std::make_shared<USER_TYRE> and everything had anticipated results, all relized functions of the app worked correctly. After that I’ve decided that I should to rewrite this app according to SOLID principles, especially to Liskov substitution principle.
I had types as expression_element, value and operators classes, all derived from the expression_element. Because of that expression_element had more mentioned virtual methods that any of the child classes has realized. And this ocasion has violated Liskov substitution principle and expression_element was available to use for the object creating. After that I have decided to make that better.
The next my step was to reorganize the classes relations: to make the classes not assumed to be used for the object creation as abstract classes, and some expression_element virtual methods should be relocated to the derived abstract classes as value_interface and operator_interface.
So I made this in the way I demonstrate below:
My file “expression_element_interface.h”:
#ifndef EXPRESSION_ELEMENT_INTERFACE_H
#define EXPRESSION_ELEMENT_INTERFACE_H
#include <memory>
class expression_element
{
private:
const int element_type;
protected:
enum {VALUE = 0, OPERATOR = 1, BRACKETED_EXPRESSION = 2, FUNCTION = 3};
expression_element(int _type) : element_type(_type) {}
virtual ~expression_element() = 0;
public:
inline int get_element_type() const {return element_type;}
bool operator==(const expression_element& it) const { return this->element_type == it.element_type; }
friend bool isvalue(const expression_element&);
friend bool isoperator(const expression_element&);
friend bool isbracked(const expression_element&);
friend bool isfunction(const expression_element&);
};
class value_interface : public expression_element
{
private:
const long double contained_value;
protected:
value_interface(long double _value) : expression_element(VALUE), contained_value(_value) {}
public:
virtual ~value_interface() = 0;
long double get_contained_value() const { return contained_value; }
friend bool samevalue(const value_interface&,const value_interface&);
};
class operator_interface : public expression_element
{
private:
const int operator_type;
protected:
enum { SUMMATOR = 0, SUBTRACTOR = 1, MULTIPLIER = 2, DIVIDER = 3};
operator_interface(int _operator_type) : expression_element(OPERATOR), operator_type(_operator_type) {}
virtual long double toCalculate(long double, long double) const = 0;
public:
virtual ~operator_interface() = 0;
virtual std::shared_ptr<value_interface> operator_apply(const std::shared_ptr<value_interface> it_1,const std::shared_ptr<value_interface> it_2) const = 0;
virtual std::shared_ptr<value_interface> operator_apply(const value_interface& it_1,const value_interface& it_2) const = 0;
inline int get_operator_type() const {return operator_type;}
friend bool issummator(const operator_interface&);
friend bool issubtractor(const operator_interface&);
friend bool ismultiplier(const operator_interface&);
friend bool isdivider(const operator_interface&);
};
#endif // EXPRESSION_ELEMENT_INTERFACE_H
My file “expression_element_interface.cpp”:
#include "expression_element_interface.h"
expression_element::~expression_element() {}
bool isvalue(const expression_element& it) { return it.element_type == expression_element::VALUE; }
bool isoperator(const expression_element& it) { return it.element_type == expression_element::OPERATOR; }
bool isbracked(const expression_element& it) { return it.element_type == expression_element::BRACKETED_EXPRESSION; }
bool isfunction(const expression_element& it) { return it.element_type == expression_element::FUNCTION; }
value_interface::~value_interface() { expression_element::~expression_element(); }
bool samevalue(const value_interface& it_1,const value_interface& it_2)
{
return it_1.contained_value == it_2.contained_value;
}
operator_interface::~operator_interface(){ expression_element::~expression_element(); }
bool issummator(const operator_interface& it) { return it.operator_type == operator_interface::SUMMATOR; }
bool issubtractor(const operator_interface& it) { return it.operator_type == operator_interface::SUBTRACTOR; }
bool ismultiplier(const operator_interface& it) { return it.operator_type == operator_interface::MULTIPLIER; }
bool isdivider(const operator_interface& it) { return it.operator_type == operator_interface::DIVIDER; }
My file “value.h”:
#ifndef VALUE_H
#define VALUE_H
#include "expression_element_interface.h"
class value : public value_interface
{
public:
value(long double _value) : value_interface(_value) {}
};
#endif // VALUE_H
My file “operator.h” (only the summator class part of all of them, other have the same structure):
class summator : public operator_interface
{
private:
long double toSum(long double _it_1, long double _it_2) const { return _it_1 + _it_2; }
long double toCalculate(long double _it_1, long double _it_2) const override { return toSum(_it_1, _it_2); }
public:
summator() : operator_interface(SUMMATOR) {}
~summator() {}
std::shared_ptr<value_interface> operator_apply(const std::shared_ptr<value_interface> it_1,const std::shared_ptr<value_interface> it_2) const override
{
return std::make_shared<value>( this->toCalculate( it_1->get_contained_value(), it_2->get_contained_value() ) );
}
virtual std::shared_ptr<value_interface> operator_apply(const value_interface& it_1,const value_interface& it_2) const override
{
return std::make_shared<value>( this->toCalculate(it_1.get_contained_value(), it_2.get_contained_value() ) );
}
};
After I restructurized my classes I started rewriting my old code to adjust it for the new data types using.
I had two more classes:
transformator_strToExpression –> to convert a const char* input in the calculator to the std::liststd::shared_ptr<expression_element>.
solver –> receive const char* input from the calculator, use the transformator_strToExpression object to convert the input to std::liststd::shared_ptr<expression_element>, save it and start the calculations (find the certain type of operator take neighbour objects and use in the method of the operator. Result value replace three object by itself: previous object, operator object, next object. Finally in std::list will remain only the one value object in the std::shared_ptr<expression_element> format).
The problem have been met in the convert step in the transformator_strToExpression class.
To explain demonstrate the conditions of this problem appear I should to show details of this class:
My file “transformator_strtoexpression.h”:
#ifndef TRANSFORMATOR_STRTOEXPRESSION_H
#define TRANSFORMATOR_STRTOEXPRESSION_H
#include "expression_element_interface.h"
#include "value.h"
#include <list>
#include <memory>
#include <QDebug> // Feel free to delete this string
class transformator_strToExpression
{
private:
std::list<std::shared_ptr<expression_element>>& convert_to_object(const char*, std::list<std::shared_ptr<expression_element>>&) const;
std::list<std::shared_ptr<expression_element>>& make_valid(std::list<std::shared_ptr<expression_element>>&) const;
//Feel free to delete this below:
void toDebug(const std::list<std::shared_ptr<expression_element>>& expression) const;
//end
public:
void transform(const char*, std::list<std::shared_ptr<expression_element>>&) const;
};
#endif // TRANSFORMATOR_STRTOEXPRESSION_H
The next my file will be a realisation of the transformator_strToExpression class definition.
All the comments have been made for myself (all “delete” comments).
And I should to mention that provided file have been changed to demonstrate this problem. Those changes you will find in transformator_strToExpression::convert_to_object() method in the if (isdigit(temp)) condition. Explanations of them will be later.
My file “transformator_strtoexpression.cpp”:
#include "transformator_strtoexpression.h"
#include "value.h"
#include "operator.h"
#include <sstream>
using elem_i = expression_element;
using array = std::list<std::shared_ptr<elem_i>>;
using oper_i = operator_interface;
using val_i = value_interface;
void transformator_strToExpression::transform(const char* _cstr, array& expression) const
{
convert_to_object(_cstr, expression);
qDebug() << "AFTER convert_to_object()";
transformator_strToExpression::toDebug(expression);
make_valid(expression);
qDebug() << "AFTER make_valid()";
transformator_strToExpression::toDebug(expression);
}
array& transformator_strToExpression::convert_to_object(const char* _cstr, array& expression) const
{
std::istringstream istream(_cstr);
char temp;
long double val;
temp = istream.peek();
while (temp != EOF)
{
temp = istream.peek();
if (isdigit(temp))
{
qDebug() << "digit"; ///delete
istream >> val;
auto b = value(val);
auto a = std::make_shared<value>(val);
std::shared_ptr<value> c (new value(val));
expression.push_back(std::static_pointer_cast<elem_i>(a));
///delete
auto j = std::static_pointer_cast<val_i>(a);
qDebug() << double(val) << " " << a->get_element_type() << ": "
<< double( a->get_contained_value() );
qDebug() << double(val) << " " << b.get_element_type() << ": "
<< double( b.get_contained_value() );
qDebug() << double(val) << " " << c->get_element_type() << ": "
<< double( c->get_contained_value() );
///delete end
}
else if (ispunct(temp))
{
switch(temp)
{
case '+':
qDebug() << "+"; ///delete
istream >> temp; expression.push_back(std::static_pointer_cast<elem_i>(std::make_shared<summator>()));
break;
case '-':
qDebug() << "-"; ///delete
istream >> temp; expression.push_back(std::static_pointer_cast<elem_i>(std::make_shared<subtractor>()));
break;
case '*':
qDebug() << "*";///delete
istream >> temp; expression.push_back(std::static_pointer_cast<elem_i>(std::make_shared<multiplier>()));
break;
case '/':
qDebug() << "/";///delete
istream >> temp; expression.push_back(std::static_pointer_cast<elem_i>(std::make_shared<divider>()));
break;
case '.':
{
qDebug() << ".";///delete
istream >> temp;
std::string str("0.");
temp = istream.peek();
if (isdigit(temp))
{
qDebug() << "->digit";///delete
istream >> val;
str += std::to_string(val);
val = std::stold(str);
expression.push_back(std::static_pointer_cast<elem_i>(std::make_shared<value>(val)));
continue;
}
}
break;
default:
qDebug() << "Transformator Error: undefined 'punct'"; istream >> temp;
}
}
else
{
qDebug() << "Transformator Error: undefined symbol"; istream >> temp;
}
}
return expression;
}
array& transformator_strToExpression::make_valid(array& expression) const
{
// EQUATION TO CALCULATIVE FORM TRANSFORMATION
if (!isvalue(**std::begin(expression))) expression.insert(std::begin(expression), std::make_shared<value>(0));
for (auto it = std::begin(expression); it != --std::end(expression); std::advance(it,1))
{
auto it_next = it;
std::advance(it_next,1);
if (*it != *std::rbegin(expression))
if ( !isvalue(**it) && !isvalue(**it_next) ) expression.erase(it);
}
auto it = std::end(expression);
std::advance(it,-1);
if (!isvalue(**it)) expression.erase(it);
return expression;
}
//Feel free to delete this below:
void transformator_strToExpression::toDebug(const array& expression) const
{
for (auto i = std::begin(expression); i != std::end(expression); std::advance(i,1))
{
if (isvalue(**i))
{
auto j = std::static_pointer_cast<val_i>(*i);
qDebug() << j->get_element_type() << ": "
<< double( j->get_contained_value() );
}
else if (isoperator(**i))
{
auto j = std::static_pointer_cast<oper_i>(*i);
qDebug() << j->get_element_type() << ": "
<< j->get_operator_type();
}
}
}
//end
So there is time to show the problem after the background have been provided:
We are going to the transformator_strToExpression::convert_to_object() method in the if (isdigit(temp)) condition:
qDebug() << "digit"; ///delete
istream >> val;
auto b = value(val);
auto a = std::make_shared<value>(val);
std::shared_ptr<value> c (new value(val));
expression.push_back(std::static_pointer_cast<elem_i>(a));
///delete
auto j = std::static_pointer_cast<val_i>(a);
qDebug() << double(val) << " " << a->get_element_type() << ": "
<< double( a->get_contained_value() );
qDebug() << double(val) << " " << b.get_element_type() << ": "
<< double( b.get_contained_value() );
qDebug() << double(val) << " " << c->get_element_type() << ": "
<< double( c->get_contained_value() );
///delete end
You can see three lines:
auto b = value(val);
auto a = std::make_shared<value>(val);
std::shared_ptr<value> c (new value(val));
They perform initialization of the value class in three way that must be identical to each other.
I verified this assertment in differnt ways:
I executed the program in the debug regime (I am doing it rigth in the same time of the writin it again).
So there is an algorithm:
I input in calculator the const char* with the content “1+1” and start calculations with button “=”.
When debug process on stage of the line auto j = std::static_pointer_cast<val_i>(a);
Those three lines has been processed.
And there are the next statment of all variables within this function:
_cstr "1+1" char*
'1' 49 0x31 char
'+' 43 0x2b char
'1' 49 0x31 char
a @0x2652910c850 std::shared_ptr<value>
[value_interface] @0x2652910c850 value_interface
[expression_element] @0x2652910c850 expression_element
[vptr] _vptr.expression_element
[0] 0x7ff731404a90 <value::get_val() const> void*
[1] 0x7ff7314049b0 <equation_element::to_calculate(long double, long double) const> void*
element_type 0 int
contained_value <invalid float value> long double
b @0xd2e4ffaf00 value
[value_interface] @0xd2e4ffaf00 value_interface
[expression_element] @0xd2e4ffaf00 expression_element
[vptr] _vptr.expression_element
element_type 0 int
contained_value 1 long double
c @0x265290b2f20 std::shared_ptr<value>
[value_interface] @0x265290b2f20 value_interface
[expression_element] @0x265290b2f20 expression_element
[vptr] _vptr.expression_element
[0] 0x7ff731404a90 <value::get_val() const> void*
[1] 0x7ff7314049b0 <equation_element::to_calculate(long double, long double) const> void*
element_type 0 int
contained_value 1 long double
expression <1 элемент> array &
[0] @0x2652910c850 std::shared_ptr<expression_element>
[value_interface] @0x2652910c850 value_interface
[expression_element] @0x2652910c850 expression_element
[vptr] _vptr.expression_element
element_type 0 int
contained_value -0.0 long double
istream @0xd2e4ffaf70 std::istringstream
j @0x80 std::shared_ptr<value_interface>
[expression_element] @0x80 expression_element
contained_value <недоступно> long double
temp '1' 49 0x31 char
this @0xd2e4ffb52f transformator_strToExpression
val 1 long double
We can see that contained_value is 1 in long double in the b and c object BUT in the a object contained_value is long double. This fact drives me crazy. How can be possible???
The next way to be assured is use the consol output, where the qDebug() helps. qDebug() can be used as the std::cout. And when i do the “1+1” input then the console output is the next:
digit
1 0 : nan
1 0 : 1
1 0 : 1
The lines for output are:
///delete
auto j = std::static_pointer_cast<val_i>(a);
qDebug() << double(val) << " " << a->get_element_type() << ": "
<< double( a->get_contained_value() );
qDebug() << double(val) << " " << b.get_element_type() << ": "
<< double( b.get_contained_value() );
qDebug() << double(val) << " " << c->get_element_type() << ": "
<< double( c->get_contained_value() );
///delete end
After that I continue the application running ant it crashes with the message in translation:
”
The application has been terminated because it received signal from the operation system.
Signal: SIGSEGV
Appointment: Segmentation fault
“
I have made everything I able to do to find the explanations for this problem. Every sourse say all auto p_obj = std::make_shared<USER_TYRE>(1);
and std::shared_ptr<USER_TYRE> p_obj (new USER_TYRE(1));
are indentical
+
auto p_obj = std::make_shared<USER_TYRE>(1);
and auto p_obj = USER_TYRE(1);
call all of the USER_TYRE constructors in the same way.
There must not be any of differece in result!
But they are!
I tried to use Copilot to explain and reveiw my code and he made the same conclusion.
I have not found any of the similar problems anywhere wherenever I tried to find.
Can someone find the explanations for this, please?
What have happened here? What is the cause of this problem? (Essentially I can start avoid std::make_shared<> for avoid this problem but that is NOT the SOLUTION of this problem and nor explanation)
Thank you for tries in advance and my respect!
P.S. If you will have some rebuke for me please say it, if there will be some recommendations how I could write code better please say it!
Pavlo Tkach is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
Check out our Code of Conduct.