certik / yaml-cpp

Automatically exported from code.google.com/p/yaml-cpp
MIT License
0 stars 0 forks source link

Make it possible to work with -fno-exceptions and -fno-rtti #260

Open GoogleCodeExporter opened 9 years ago

GoogleCodeExporter commented 9 years ago
I would like to integrate yaml-cpp 0.5.1 into a existing project with C++ 
exceptions disabled but it is not possible right now due to compile-time errors 
like this:

./yaml-cpp/include/yaml-cpp/node/impl.h: In member function 'void 
YAML::Node::EnsureNodeExists() const':                                
./yaml-cpp/include/yaml-cpp/node/impl.h:56:31: error: exception handling 
disabled, use -fexceptions to enable                           
             throw InvalidNode();

Could you please add support for -fno-exceptions into yaml-cpp as it is done 
i.e. by the libstdc++?

Here is a short explanation how it is done in libstdc++:
* http://gcc.gnu.org/onlinedocs/libstdc++/manual/using_exceptions.html
Example header with wrapper functions that throw STL exceptions:
* http://gcc.gnu.org/onlinedocs/libstdc++/latest-doxygen/a01203.html

Original issue reported on code.google.com by freej...@tlen.pl on 10 Oct 2014 at 10:16

GoogleCodeExporter commented 9 years ago
The main problem is that yaml-cpp uses exceptions as the only way of reporting 
parsing errors. I believe libstd has no analogous reliance on exceptions.

What would you suggest we do instead?

Original comment by jbe...@gmail.com on 10 Oct 2014 at 2:28

GoogleCodeExporter commented 9 years ago

Let's assume that __throw_XXX substitutes each direct call to `throw`, so that 
it calls `throw` in enabled exceptions and aborts in disabled exceptions (as it 
is done in libstdc++).

We have parser and emitter functionalities, each of them uses exceptions in a 
slightly different way. Parser takes a string and parses it into a data 
structure, it is a kind of an "atomic" operation:

  std::ifstream fin("test.yaml");
  YAML::Parser parser(fin); // this may fail or succeed, throws if fails, no insight into internals
  YAML::Node doc;
  parser.GetNextDocument(doc); // this may fail or succeed, throws if fails

...but with exceptions disabled:

  std::ifstream fin("test.yaml");
  YAML::Parser parser(fin); // *never throws*
  YAML::Node doc;

  // if data to be parsed was malformed:
  //   an attempt to call any member function of parser that expects
  //   correctly parsed `fin` will call __throw_XXX and assert/abort
  if (true == parser.good())
  {
      parser.GetNextDocument(doc);
  }

  parser.GetNextDocument(doc); // may call __throw_XXX 

On the other hand, in the emitter case user builds YAML document step by step 
and __throw_XXX should be called upon the first error occurs.

Code like this seems to be design-by-contract-alike

  inline bool Node::IsDefined() const
  {
      if(!m_isValid)
          throw InvalidNode();

and `throw` may be directly replaced with `assert`. Or even better, with 
`__throw_InvalidNode` that calls assert/abort if exceptions are disabled, but 
throws InvalidNode() if they are not. Similarly, in switch-case constructs you 
may issue ILLEGAL_CASE (or ILLEGAL_PATH) and assert/throw on invalid label is 
entered.

Code in include/* and src/* never catches thrown exceptions, that makes things 
easier to convert that code to work with -fno-exceptions.

Original comment by freej...@tlen.pl on 13 Oct 2014 at 10:31