RealTimeBiomechanics / rtosim

The real-time OpenSim extension (RTOSIM) is a set of libraries in C++ that wrap OpenSim APIs to enable the real-time computation of inverse kinematics and inverse dynamics.
Apache License 2.0
33 stars 14 forks source link

rtosim_ik_id_from_nexus.exe cannot read command line argument values if their names include whitespaces #16

Open jerela opened 4 years ago

jerela commented 4 years ago

Hi, This is something I noticed while trying to run my own data with rtosim_ik_id_from_nexus.exe: If you run the executable from the command line and provide paths with parameters and those paths include whitespaces, for example rtosim_ik_id_from_nexus.exe --model "C:/Folder Name/myModel.osim" then mainInverseKinematicsInverseDynamicsFromNexus.cpp fails to read the argument correctly because po.getParameter("--model"); will only return "C:/Folder", meaning that the whitespace in the name ends the string. This is not a major issue, but I thought I'd bring it up anyway. I haven't checked the other executables to see if they have a similar issue, but if they accept command line arguments similarly, they probably do.

cpizzolato commented 4 years ago

Thanks for flagging this. The parser I have implemented is very minimalist rtosim/lib/Utilities/rtosim/ProgramOptionsParser.h and does not deal with any special case (whitespeces, quotes, etc..). It could be possible to implement a decent tokenizer in ProgramOptionsParser::ProgramOptionsParser(int argc, char **argv), and everything else should fall in place. Any suggestion?

jerela commented 4 years ago

I tried editing ProgramOptionsParser.h for a while, but I'm not a C++ wiz and eventually gave up. I edited T ProgramOptionsParser::getParameter so that it would get the strings between two arguments (like the file path between --model and another option), but I couldn't get stringstream to write several strings separated by whitespaces to ans. I also tried to get the strings between two arguments as a string type variable instead of using stringstream, but then I was once again faced with having to use stringstream to write it to ans because a string type variable wasn't a valid return variable type, and the original problem of whitespaces reoccurred.

cpizzolato commented 4 years ago

Hi @jerela thanks for giving it a go. The way I would do it is working on the constructor, where the parsing of the program arguments actually happens. I would probably retokenise the whole string. This is a snippet from an old piece of code where I was accounting for quotes so that everything between quotes would be considered together. Could be a good starting point.

    std::vector<std::string> tokenize(const std::string& line, char sep = ',') {

    std::vector<std::string> result;
    std::size_t first(0), last(0);
    if (line.find(sep) == std::string::npos)
      result.emplace_back(line);
    else {
      while (last < line.size()) {
        if (line[last] == '\"') {
          last++;
          last = line.substr(last, line.size() - last).find('\"') + last + 1;
        }
        else if (line[last] == sep) {
          result.emplace_back(line.substr(first, last - first));
          first = ++last;
        }
        else
          ++last;
      }
      if (last != first)
        result.emplace_back(line.substr(first, last - first));
    }
    return result;
  }
jerela commented 4 years ago

Hi, thanks for the example. I will see if I have time to look into this, but currently I'm fairly occupied with other things so I can't make any promises.