devosoft / Empirical

A library of tools for scientific software development, with emphasis on also being able to build web interfaces using Emscripten.
Other
86 stars 38 forks source link

`emp::vector` and `std::vector` interoperability #214

Open mmore500 opened 5 years ago

mmore500 commented 5 years ago

compiles:

#include <string>

#include "base/vector.h"
#include "config/command_line.h"

int main(int argc, char* argv[])
{
  emp::vector<std::string> args = emp::cl::args_to_strings(argc, argv);
  const bool verbose = emp::cl::use_arg(args, "-v");
  return 0;
}

won't compile:

#include <string>

#include "config/command_line.h"

int main(int argc, char* argv[])
{
  std::vector<std::string> args = emp::cl::args_to_strings(argc, argv);
  const bool verbose = emp::cl::use_arg(args, "-v");
  return 0;
}

giving the compilation error message

╭  ThinkPad |  mmore500  ~                                                                                                                          ✔  21:45:32  
╰ g++ -IProjects/Empirical/source hello.cpp
hello.cpp: In function ‘int main(int, char**)’:
hello.cpp:8:51: error: cannot bind non-const lvalue reference of type ‘emp::vector<std::__cxx11::basic_string<char> >&’ to an rvalue of type ‘emp::vector<std::__cxx11::basic_string<char> >’
   const bool verbose = emp::cl::use_arg(args, "-v");
                                                   ^
In file included from Projects/Empirical/source/config/command_line.h:38:0,
                 from hello.cpp:3:
Projects/Empirical/source/config/../base/vector.h:162:5: note:   after user-defined conversion: emp::vector<T, Ts>::vector(const stdv_t&) [with T = std::__cxx11::basic_string<char>; Ts = {}; emp::vector<T, Ts>::stdv_t = std::vector<std::__cxx11::basic_string<char> >]
     vector(const stdv_t & in) : stdv_t(in), revision(1) { ; }         // Emergency fallback conversion.
     ^~~~~~
In file included from hello.cpp:3:0:
Projects/Empirical/source/config/command_line.h:67:10: note:   initializing argument 1 of ‘bool emp::cl::use_arg(emp::vector<std::__cxx11::basic_string<char> >&, const string&)’
     bool use_arg(emp::vector<std::string> & args, const std::string & pattern) {
mmore500 commented 5 years ago

issue seems to be related to emp::vector<std::string> vs. std::vector<std::string>

mercere99 commented 5 years ago

When we are not in debug mode, the emp::vector becomes an std::vector, thus allowing them to be used interchangable. When we are in debug mode, emp::vector becomes a class derived from std::vector, and thus can be passed in anywhere the an std::vector is required. The reverse, however, is not true and not particularly trivial since emp::vector has extra internal state.

I think from my own perspective, I was assuming that if people are using an emp function, they'll be using emp::vector, but that's not very friendly to those who want to port their code over.

I'm not actually sure of the best way to solve this problem; anyone else have ideas?

(This is also relevant since I'm currently building a similar emp drop-in replacement for map that will find other types of common bugs.)

mmore500 commented 5 years ago

Maybe we could provide a user-defined conversion from emp::vector to std::vector... something a la

 operator std::vector<T>() const { TODO }

As mentioned, we'd lose internal state information so perhaps we could print a run time warning when the conversion is used?

mercere99 commented 5 years ago

Unfortunately the conversion won't help in most cases. Vectors tend to be passed around as references for speed purposes (either because you don't want to take the time to copy the whole thing or because the function you're calling with it might change it).

I am toying with some other ideas though. We might be able to use a move constructor to build an emp::vector on the fly from the std::vector and then pass the contents back again.

mmore500 commented 5 years ago

Would that help in this case? My understanding is that the snipped above doesn't compile because we need to get a std::vector (e.g., std::vector<std::string> args) from an emp::vector (e.g., the return type of emp::cl::args_to_strings()) and not the other way around

mercere99 commented 5 years ago

That direction works; the first line of main() compiles fine. Since emp::vector is derived from std::vector the std::vector constructor will automatically convert it back (and an operator std::vector isn't needed).

The SECOND line, however, is where the problem is. In the failing example, we're trying to pass an std::vector into a function that is expecting a reference to an emp::vector and we can't do that kind of conversion.

mmore500 commented 5 years ago

Ah, I did misunderstand. That makes a lot more sense now...

mercere99 commented 5 years ago

The specific conversion point may be off, but you're exactly right that we need to think more through the nature of these conversions.