News for hackers. Stuff that matters.
Re:Why C++ sucks (2016 edition) by loup-vaillant on Monday February 22, 2016 @04:22AM (#11149579)
> It has the best performance and the highest level abstractions in one package.

The highest level abstractions, are you kidding me?

I am currently writing a little interpreter in C++. I would have used Ocaml, but the rest of the project is in C++. Well, just try to write an abstract syntax tree in C++. In languages such as ML and Haskell, this is easy:

  type expression = Litteral    of value
                  | Symbol      of string
                  | Funcall     of expression * expression
                  | Conditional of expression * expression * expression
                  | While_loop  of expression * expression
                  | Sequence    of expression list
And I'm pretty much done. Now in C++:

  --------------------------------------------------------------------------------
  #ifndef __Expression_h__
  #define __Expression_h__
  
  #include <cstdint>
  #include <string>
  #include "Value.h"
  
  
  namespace Script {
  
  class Expression;
  
  class Expression {
  public:
      enum Tag {
          litteral,
          symbol,
          funcall,
          conditional,
          while_loop,
          sequence
      };
  
      union Val {
          Value                   *litteral;
          std::string             *symbol; // or a true symbol?
          std::vector<Expression> *composite;
      };
  
      Expression(const Value&);
      Expression(const std::string&);
      Expression(Tag, std::vector<Expression>*); // takes ownership
      ~Expression();
  
  private:
      Tag _tag;
      Val _val;
  };
  
  } // namespace Script
  
  #endif // __Expression_h__
  --------------------------------------------------------------------------------
Don't forget the cpp file either:

  --------------------------------------------------------------------------------
  #include "Expression.h"
  #include <cstdio>

  using namespace Script;
  
  Expression::Expression(const Value& val)
      : _tag(litteral)
  {
      _val.litteral = new Value(val);
  }
  
  Expression::Expression(const std::string& sym)
      : _tag(symbol)
  {
      _val.symbol = new std::string(sym);
  }
  
  Expression::Expression(Tag tag, std::vector<Expression>* exprs)
  {
      if ((tag == funcall     && exprs->size() == 2) ||
          (tag == conditional && exprs->size() == 3) ||
          (tag == while_loop  && exprs->size() == 2) ||
          tag == sequence) {
          _tag = tag;
          _val.composite = exprs;
      } else {
          std::fprintf(stderr, "Expression constructor: Invalid Expression\n");
          exit(1);
      }
  }
  
  Expression::~Expression()
  {
      switch (_tag) {
      case litteral   :
          delete _val.litteral;
          break;
      case symbol     :
          delete _val.symbol;
          break;
      case funcall    :
      case conditional:
      case while_loop :
      case sequence  :
          delete _val.composite;
          break;
      default:
          std::fprintf(stderr, "Expression destructor: broken class invariant\n");
          exit(1);
      }
  }
  --------------------------------------------------------------------------------
I may use smart pointers instead of doing my own RAII by hand, but that's still a hassle. Some may point out that my ugly unsafe union type is not the way to to this, I should use a class hierarchy and polymorphism. But just imagine the sheer amount of code I'd have to write.

C++ is a low level language. It's high level pretense is nothing but a thin veneer that cracks under the slightest scratch. Or, someone show me how to implement an abstract syntax tree in less than 20 lines of code. I'm lenient: Haskell and ML only need 6.

(Yes, I'm using Sum types, a feature that's pretty much unique to ML languages. But no, that's not cheating: providing enumeration and unions but somehow failing to combine them into sum types is a serious oversight.)

Re:Why C++ sucks (2016 edition) by userbinator on Monday February 22, 2016 @04:48AM (#11149686)
I think you're deliberately making it more verbose than it needs to be. The class definition could be condensed into:

    class Expression {
      enum Tag { litteral, symbol, funcall, conditional,
       while_loop, sequence } _tag;
      union {
       Value *litteral;
       std::string *symbol; // or a true symbol?
       std::vector<Expression> *composite;
      } _val;
    };
And you could probably use templates or macros to autogenerate most of those constructors/destructors automatically.
Re:Why C++ sucks (2016 edition) by loup-vaillant on Monday February 22, 2016 @05:12AM (#11149772)
> I think you're deliberately making it more verbose than it needs to be.

I swear I am not. This is will be production code, for which I earn my salary. Your code is terser, but still fundamentally the same as mine. It's still unsafe (we could access the wrong union member), and it still has to work around incomplete definitions and the lack of trivial constructors (I wouldn't use pointers otherwise).

Automating my constructors and destructors would be amazing. Unfortunately, I don't know how to do it —not without writing over a hundred lines of cryptic boilerplate first.

Re:Why C++ sucks (2016 edition) by fsloth on Monday February 22, 2016 @05:10AM (#11149763)
The ML example was missing the second half of the ML niceness - efficient pattern matching at syntax levels which makes for a really nice and understandable code, especially when writing complex compound clauses.

"...could probably use templates or macros to autogenerate.."

... which kind of highlights that ML example even more. In Ocaml or F# that original definition would have been more or less enough to start writing nice, efficient parsing code.

The sum types are not the only thing, IMO, that ML-family has going for it. As for example F# contains pairs and lists as syntax level constructs (rather than library add-ons) lot of code can be written at a really high level before optimization - in the same language - if it's needed.

Re:Why C++ sucks (2016 edition) by jcelerier on Monday February 22, 2016 @06:04AM (#11149982)
Re:Why C++ sucks (2016 edition) by blub on Monday February 22, 2016 @07:52AM (#11150448)
With Boost Spirit you can write expressions akin to EBNF in your C++ code. The downsides are compile times (improved in the new version) and difficult to parse error messages at compile time.

If you stick to your code you should at least:

* add a copy constructor or delete it explicitly

* pass in the vector as unique_ptr to the constructor. This way the "takes ownership" will be clear without the need of a comment.

* add your missing includes e.g. vector

* stop using _ as variable prefix, it's forbidden

* throw from the constructor, don't exit

Boost variant + boost vector will allow you to eliminate all the manual memory management (boost vector allows incomplete types).

Re:Why C++ sucks (2016 edition) by loup-vaillant on Monday February 22, 2016 @08:44AM (#11150707)
Thanks for the tips, I'll apply them. Just a couple questions:

* Do you have a link about the _ (underscore) being forbidden? Our current coding rule make us use them right now.

* Is there a standard exception object I could throw? If I could just write something like `throw std::invalid_argument("what String");` that would be terrific. I don't want to construct a whole class just for that. (Maybe I have to, though?)

I'll also seriously consider boost. While it's a huge dependency, we are on Debian GNU/Linux, so it's probably not that bad.

Re:Why C++ sucks (2016 edition) by blub on Monday February 22, 2016 @08:56AM (#11150776)
You're welcome. The underscore thing is mentioned in 17.6.4.3.2 Global names, however it's more complex than what I said initially (this is C++ after all):

* double underscore or underscore + capital letter is reserved in any scope

* underscore is reserved in global scope

Your code is allowed in fact, sorry. My personal rule is that I never start an identifier with an underscore anywhere.

Re:Why C++ sucks (2016 edition) by blub on Monday February 22, 2016 @11:53AM (#11152210)
I missed the exc question: yes, invalid_argument is perfectly acceptable in my opinion. However, if you want to inherit from invalid_arg that is also possible - this would allow you to catch only this very specific type of error.
Re:Why C++ sucks (2016 edition) by gpderetta on Monday February 22, 2016 @05:32AM (#11149854)
You could consider using any of the already existing sum type libraries like boost::variant.

The code will certainly be more verbose than ML, but still better than roll-your-own.

Re:Why C++ sucks (2016 edition) by loup-vaillant on Monday February 22, 2016 @05:46AM (#11149914)
And manage another significant dependency. If boost::variant was as nice as ML, I would do it. But it's not. I'm not sure this is worth the trouble.
Re:Why C++ sucks (2016 edition) by gpderetta on Monday February 22, 2016 @05:59AM (#11149962)
If you are already using boost, as many projects are, it is no extra dependency. Otherwise there are many standalone implementations.

Egg.Variant is an high quality one: https://github.com/eggs-cpp/variant .

We are likely going to get a std variant type in the next standard, either as a library component or as a language feature.

Re:Why C++ sucks (2016 edition) by ksherlock on Monday February 22, 2016 @09:24AM (#11150930)
c++17 might include a variant template which would replace all that (or at least the backing storage for it). (std::variant<Value, std::string, std::vector<Expression *>>)
Re:Why C++ sucks (2016 edition) by Joky on Monday February 22, 2016 @06:43PM (#11155291)
So the C++ way is either to write a code generator for your own DSL, or to write a library that would allow to write you grammar in an eDSL fashion. No pain, no gain ;)
Re:Why C++ sucks (2016 edition) by masklinn on Monday February 22, 2016 @09:04AM (#11150817)
> Yes, I'm using Sum types, a feature that's pretty much unique to ML languages.

That's a bit too restrictive. Rust, Swift or Kotlin are not MLs.

Re:Why C++ sucks (2016 edition) by loup-vaillant on Monday February 22, 2016 @02:20PM (#11153485)
Good point. But we should let them age a bit. Right now they're a bit young for production code with deadlines and all. I hate C++, but I like the certainty that comes with tools I know.
Re:Why C++ sucks (2016 edition) by b169118 on Monday February 22, 2016 @05:19AM (#11149797)
Why do you need to use pointers for string and vector? Aren't they already heap allocated internally?
Re:Why C++ sucks (2016 edition) by loup-vaillant on Monday February 22, 2016 @05:42AM (#11149892)
My code compiles as is, so you can try yourself if you want.

If I didn't use pointers, I would have 2 errors. One would be about the use of an incomplete data type (Expression), and the other about trying to put something that doesn't have a "trivial constructor" in a union (String, Value, and std::vector<Expression>).

I can't use references either, because I would have to initialise them in the initialisation list of my constructor, which I can't do until I know the tag. I could still initialise the tag first, but that's specified by the order in which the data members are defined, rather than the order of the initialisation list —a rather dangerous idiom to rely on, should anyone modify the code.

Re:Why C++ sucks (2016 edition) by gpderetta on Monday February 22, 2016 @09:56AM (#11151139)
Note that in C++11 is perfectly fine to put a type with a non trivial constructor in an union.

You are right about the issue with std::vector and incomplete types. It will be possibly resolved in the next standard. In the meantime you can use something like boost::container::vector which explicitly support incomplete types.

Re:Why C++ sucks (2016 edition) by ksherlock on Monday February 22, 2016 @09:28AM (#11150960)
It's extra work, but you could use std::aligned_union<1, std::string, std::vector<Expression>, ...> to declare space for the union and then use placement new/manual destructor calls to initialize and destroy.