4

I'm trying to use the ExprTk mathematical expression parser library within a class whose objects are to be stored in a vector of objects, which is a member variable of another class; however, when I try to push_back a new object in the vector I'm getting a lot of "use of deleted function" errors. Here is a simple version of the code that is giving me problems:

#include <exprtk.hpp>
#include <iostream>
#include <string>
#include <vector>

class B {
public:
  double x;
  exprtk::symbol_table<double> symbol_table;
  exprtk::parser<double> parser;
  exprtk::expression<double> expr_obj;

  B();
};

class A {
public:

  std::vector<B> Bvec;

  A();
};

A::A() {
  Bvec.push_back(B());
};

B::B() {
  symbol_table.add_variable("x", x);
  expr_obj.register_symbol_table(symbol_table);
  parser.compile("x^2",expr_obj);

  x = 2.0;
  std::cout << expr_obj.value() << std::endl;

}

int main(int argc, char const* argv[]) {

  A a_obj;

  return 0;
}

I haven't included the header library since it's nearly 40,000 lines, but it can be found here: http://www.partow.net/programming/exprtk/.

Here is the error message

In file included from /usr/include/x86_64-linux-gnu/c++/7/bits/c++allocator.h:33:0,
                 from /usr/include/c++/7/bits/allocator.h:46,
                 from /usr/include/c++/7/string:41,
                 from /usr/include/c++/7/bits/locale_classes.h:40,
                 from /usr/include/c++/7/bits/ios_base.h:41,
                 from /usr/include/c++/7/ios:42,
                 from /usr/include/c++/7/ostream:38,
                 from /usr/include/c++/7/iostream:39,
                 from src/main.cpp:1:
/usr/include/c++/7/ext/new_allocator.h: In instantiation of ‘void __gnu_cxx::new_allocator<_Tp>::construct(_Up*, _Args&& ...) [with _Up = B; _Args = {B}; _Tp = B]’:
/usr/include/c++/7/bits/alloc_traits.h:475:4:   required from ‘static void std::allocator_traits<std::allocator<_CharT> >::construct(std::allocator_traits<std::allocator<_CharT> >::allocator_type&, _Up*, _Args&& ...) [with _Up = B; _Args = {B}; _Tp = B; std::allocator_traits<std::allocator<_CharT> >::allocator_type = std::allocator<B>]’
/usr/include/c++/7/bits/vector.tcc:100:30:   required from ‘void std::vector<_Tp, _Alloc>::emplace_back(_Args&& ...) [with _Args = {B}; _Tp = B; _Alloc = std::allocator<B>]’
/usr/include/c++/7/bits/stl_vector.h:954:21:   required from ‘void std::vector<_Tp, _Alloc>::push_back(std::vector<_Tp, _Alloc>::value_type&&) [with _Tp = B; _Alloc = std::allocator<B>; std::vector<_Tp, _Alloc>::value_type = B]’
src/main.cpp:25:21:   required from here
/usr/include/c++/7/ext/new_allocator.h:136:4: error: use of deleted function ‘B::B(B&&)’
  { ::new((void *)__p) _Up(std::forward<_Args>(__args)...); }
    ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
src/main.cpp:6:7: note: ‘B::B(B&&)’ is implicitly deleted because the default definition would be ill-formed:
 class B {
       ^
src/main.cpp:6:7: error: ‘exprtk::parser<T>::parser(const exprtk::parser<T>&) [with T = double]’ is private within this context
In file included from src/main.cpp:3:0:
ext_libs/exprtk/exprtk.hpp:35289:7: note: declared private here
       parser(const parser<T>&);
       ^~~~~~
In file included from /usr/include/c++/7/bits/stl_tempbuf.h:60:0,
                 from /usr/include/c++/7/bits/stl_algo.h:62,
                 from /usr/include/c++/7/algorithm:62,
                 from ext_libs/exprtk/exprtk.hpp:37,
                 from src/main.cpp:3:
/usr/include/c++/7/bits/stl_construct.h: In instantiation of ‘void std::_Construct(_T1*, _Args&& ...) [with _T1 = B; _Args = {B}]’:
/usr/include/c++/7/bits/stl_uninitialized.h:83:18:   required from ‘static _ForwardIterator std::__uninitialized_copy<_TrivialValueTypes>::__uninit_copy(_InputIterator, _InputIterator, _ForwardIterator) [with _InputIterator = std::move_iterator<B*>; _ForwardIterator = B*; bool _TrivialValueTypes = false]’
/usr/include/c++/7/bits/stl_uninitialized.h:134:15:   required from ‘_ForwardIterator std::uninitialized_copy(_InputIterator, _InputIterator, _ForwardIterator) [with _InputIterator = std::move_iterator<B*>; _ForwardIterator = B*]’
/usr/include/c++/7/bits/stl_uninitialized.h:289:37:   required from ‘_ForwardIterator std::__uninitialized_copy_a(_InputIterator, _InputIterator, _ForwardIterator, std::allocator<_Tp>&) [with _InputIterator = std::move_iterator<B*>; _ForwardIterator = B*; _Tp = B]’
/usr/include/c++/7/bits/stl_uninitialized.h:311:2:   required from ‘_ForwardIterator std::__uninitialized_move_if_noexcept_a(_InputIterator, _InputIterator, _ForwardIterator, _Allocator&) [with _InputIterator = B*; _ForwardIterator = B*; _Allocator = std::allocator<B>]’
/usr/include/c++/7/bits/vector.tcc:426:6:   required from ‘void std::vector<_Tp, _Alloc>::_M_realloc_insert(std::vector<_Tp, _Alloc>::iterator, _Args&& ...) [with _Args = {B}; _Tp = B; _Alloc = std::allocator<B>; std::vector<_Tp, _Alloc>::iterator = __gnu_cxx::__normal_iterator<B*, std::vector<B> >; typename std::_Vector_base<_Tp, _Alloc>::pointer = B*]’
/usr/include/c++/7/bits/vector.tcc:105:21:   required from ‘void std::vector<_Tp, _Alloc>::emplace_back(_Args&& ...) [with _Args = {B}; _Tp = B; _Alloc = std::allocator<B>]’
/usr/include/c++/7/bits/stl_vector.h:954:21:   required from ‘void std::vector<_Tp, _Alloc>::push_back(std::vector<_Tp, _Alloc>::value_type&&) [with _Tp = B; _Alloc = std::allocator<B>; std::vector<_Tp, _Alloc>::value_type = B]’
src/main.cpp:25:21:   required from here
/usr/include/c++/7/bits/stl_construct.h:75:7: error: use of deleted function ‘B::B(B&&)’
     { ::new(static_cast<void*>(__p)) _T1(std::forward<_Args>(__args)...); }
       ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Makefile:29: recipe for target 'obj/src/main.o' failed
make: *** [obj/src/main.o] Error 1

I'm pretty sure the issue has something to do with the push_back function and how the object is being copied; however, that's a bit over my head and knowledge of c++ (which is rather basic).

Any help would be appreciated. Thanks!

mrmudd
  • 61
  • 5
  • Basic gist is somewhere you're probably copying something that does not permit copying. Probbaly a pass by value where a pass by reference is required or a [copy initialization](https://en.cppreference.com/w/cpp/language/copy_initialization) when compiling to an older compiler standard. Note: `std::vector` does a LOT of copying, moving and assigning. Everything you put in a `vector` needs to at least be movable, and `Bvec.push_back(B());` will make a copy. – user4581301 Oct 15 '20 at 17:56
  • 1
    Thanks for the update. `Parser` cannot be copied, and based on the signature of how copying was prevented, the copy constructor was made `private`, it's unlikely that it can be moved either (`private` copy constructers were what you used before the disabling of a special member function with the `delete` keyword was added to the language in C++11, the same time move semantics were added). You cannot have a `Parser` instance as a member of an object you want to copy (unless you're going to get really weird). – user4581301 Oct 15 '20 at 18:35
  • This is all enforcement of you not wanting to have multiple instance of the same `Parser` floating around. You will have to use a reference, most likely a pointer because references are a to copy assign, to a single instance instead. – user4581301 Oct 15 '20 at 18:35
  • And Why am I writing this in comments again? When will I realize I can ing answer questions? – user4581301 Oct 15 '20 at 18:36
  • 1
    OK. I'm OK with comments for now. Need more information. Does `B` need to keep the parser around? Could you not make it a local variable in the `B` constructor and simply store the result? If so, so you need `B` at all? What about simply popping the result of parsing into the `vector`? – user4581301 Oct 15 '20 at 18:42
  • Wow, what a simple solution I never would have figured out on my own. Making the parser a local variable in the constructor works perfectly! I don't need it after the .compile function. By the way, this was a contrived example of a much longer code - I do need the analogs of A and B in the real code. Thanks so much! – mrmudd Oct 15 '20 at 19:04

2 Answers2

3

Looking at the ExprTk documentation (readme.txt), specifically Section 10.3 we have the following note:

Note:  The  exprtk::parser  is  a  non-copyable  and  non-thread  safe
component, and should only be shared via either a reference, a  shared
pointer  or  a  std::ref  mechanism,  and  considerations  relating to
synchronisation  taken  into  account  where  appropriate.  The parser
represents an object factory,  specifically a factory of  expressions,
and generally should  not be instantiated  solely on a  per expression
compilation basis.

Section 10.3

2

parser cannot be copied, and based on the signature of how copying was prevented, the copy constructor was made private, it's unlikely that it can be moved either (private copy constructers were what you used before the disabling of a special member function with the delete keyword was added to the language in C++11, the same time move semantics were added). You cannot have a exprtk::parser instance as a member of an object you want to copy (unless you're going to get really weird in custom special member functions and NOT copy the exprtk::parser).

This is all enforcement of you not wanting to have multiple instance of the same Parser floating around. You will have to use a reference, most likely a smart pointer because references are a <expletive deleted> to copy assign, to a single instance instead.

But this raises the question of whether you need to keep parser around as a member at all. What about something like this:

class B {
public:
  double x; // Not sure we need even this.
  double result;
  B();
};

B::B():
{
  // parser is handled with local variables.
  exprtk::symbol_table<double> symbol_table;
  exprtk::parser<double> parser;
  exprtk::expression<double> expr_obj;

  symbol_table.add_variable("x", x);
  expr_obj.register_symbol_table(symbol_table);
  parser.compile("x^2",expr_obj);

  x = 2.0;
  result = expr_obj.value(); // store instead of printing
} // parser and friends are no longer needed and discarded.
user4581301
  • 33,082
  • 7
  • 33
  • 54