Go for C++ Programmers(github.com)
github.com
Go for C++ Programmers
https://github.com/golang/go/wiki/GoForCPPProgrammers
56 comments
This article comes up every so often on HN, and it's great, but the people citing it tend to muddy its argument, as you've done here.
The "exploit" rsc is referring to here is hypothetical.
To wit: if there existed some service (like multithreaded GAE, or an especially weird browser) that allowed content-controlled Golang code to run, that code could exploit mutability to violate assumptions made by other code, in much the same way as you could by dropping down to "unsafe" in Golang and Rust.
For that vulnerability to matter, you have to suppose a system that rigorously constrains the runtime to block system calls, containerize I/O, shut off the "unsafe" APIs, and somehow fence off state for crypto. Nothing does that. Real-world systems that allow users to upload arbitrary Golang code give up on protecting the runtime, and use OS-level protections to maintain integrity.
I agree that Golang isn't a great fit for C++ programmers. If you're still writing C++ in 2015, it's because you're productive using all the bells and whistles C++ provides. Golang is very much a one bell, no whistle language.
The "exploit" rsc is referring to here is hypothetical.
To wit: if there existed some service (like multithreaded GAE, or an especially weird browser) that allowed content-controlled Golang code to run, that code could exploit mutability to violate assumptions made by other code, in much the same way as you could by dropping down to "unsafe" in Golang and Rust.
For that vulnerability to matter, you have to suppose a system that rigorously constrains the runtime to block system calls, containerize I/O, shut off the "unsafe" APIs, and somehow fence off state for crypto. Nothing does that. Real-world systems that allow users to upload arbitrary Golang code give up on protecting the runtime, and use OS-level protections to maintain integrity.
I agree that Golang isn't a great fit for C++ programmers. If you're still writing C++ in 2015, it's because you're productive using all the bells and whistles C++ provides. Golang is very much a one bell, no whistle language.
For that vulnerability to matter, you have to suppose a system that rigorously constrains the runtime to block system calls, containerize I/O, shut off the "unsafe" APIs, and somehow fence off state for crypto. Nothing does that.
Google AppEngine does exactly that.
Google AppEngine does exactly that.
It just means that Go isn't memory-safe by default.
I have already replaced C++. C++14 has already replaced C++98 for me.
Can you ban "native"/bare pointers in C++14? And C-style casts?
-Werror=old-style-cast will make C casts an error in gcc and clang.
The one time I still use C casts in C++ is when comparing an int against the size of a container:
I don't want to have to make the variable "i" unsigned, because using unsigneds for anything other than bit arithmetic is quite hazardous in practice (http://soundsoftware.ac.uk/c-pitfall-unsigned).
How do others resolve this problem?
if (i < (int)container.size())
This is because container.size() returns an unsigned type, and I try to avoid unsigneds in everyday code, and I like to use -Wall -Werror which, amongst other things, produces errors for signed/unsigned comparison warnings.I don't want to have to make the variable "i" unsigned, because using unsigneds for anything other than bit arithmetic is quite hazardous in practice (http://soundsoftware.ac.uk/c-pitfall-unsigned).
How do others resolve this problem?
I stopped doing this once 64 bits became a thing. There's nothing wrong per se with having your indexes signed, if you don't mind potentially not being able to access the entire array - though I'm not convinced using int is very forward-thinking, as they're fairly reliably still 32 bits - but there's everything wrong with casting size_t to int.
For example, assuming 64 bit, suppose i is 0x1000 and container.size() is 0x100000fff. Now (int)container.size() is very likely to be 0xfff, meaning i is greater, and something goes wrong.
Or - and this would be very likely true for 32 bit systems as well - suppose container.size() is 0x80000000. Now (int)container.size() is -2147483648, meaning i is greater, and something goes wrong again.
A better option would be (assuming size_t is at least as wide as int, which is vanishingly unlikely not to be true on modern systems, and can anyway be checked for using a static assert) i>=0&&(size_t)i<container.size(). Then you know the cast won't produce nonsense.
For example, assuming 64 bit, suppose i is 0x1000 and container.size() is 0x100000fff. Now (int)container.size() is very likely to be 0xfff, meaning i is greater, and something goes wrong.
Or - and this would be very likely true for 32 bit systems as well - suppose container.size() is 0x80000000. Now (int)container.size() is -2147483648, meaning i is greater, and something goes wrong again.
A better option would be (assuming size_t is at least as wide as int, which is vanishingly unlikely not to be true on modern systems, and can anyway be checked for using a static assert) i>=0&&(size_t)i<container.size(). Then you know the cast won't produce nonsense.
That is a good point. The motivation for casting the size to int, rather than the other way around, is to get it out of "unsigned world" (where people often reason badly about even simple arithmetic) and into "signed world" as simply as possible. Often where this is going on, the container is expected to be small, but of course that's a hazardous intuition as well.
Maybe I need a little helper:
Maybe I need a little helper:
template<typename T, typename C>
bool is_in_range(T i, const C &container)
{
if (i < 0) return false;
if (sizeof(T) > sizeof(typename C::size_type)) {
return i < static_cast<T>(container.size());
} else {
return static_cast<typename C::size_type>(i) < container.size();
}
}
then if (is_in_range(i, container)) ...You've traded one hazard for another there, now you have a bug at runtime if the container size is between the range of signed int and unsigned int. Hope that container size isn't user controlled.
I'm guilty of a few C-style casts, but certainly prefer catching things at compile time than runtime. So +1 unsigned types for me. At least the compiler helps with catching signedness issues.
I'm guilty of a few C-style casts, but certainly prefer catching things at compile time than runtime. So +1 unsigned types for me. At least the compiler helps with catching signedness issues.
Does the compiler help?
$ cat unsigned.cpp
#include <iostream>
void f(unsigned int x) { std::cout << "x = " << x << std::endl; }
int main()
{
f(-1);
}
$ g++ -Wall -Wextra -Wconversion -Werror unsigned.cpp
$ ./a.out
x = 4294967295
No error, no warning. The two are not really distinct types. See the link I posted above for more argumentation along these lines.You should use static_cast<>() and not C style cast.
You should not care about signed/unsigned, use auto.
You should not care about signed/unsigned, use auto.
I would prefer to use auto when iterating through the container, certainly. But much of the code I write is numerical stuff and "i" really is logically an integer rather than an iterator, arising as the result of some actual arithmetic.
I find I have to do this quite often, and I'm not going to write static_cast<int>() every time.
Yes, I can see why this isn't a good idea -- I'm pained by it myself. I'm just wondering what I might have overlooked among the possibilities available for this kind of code.
I find I have to do this quite often, and I'm not going to write static_cast<int>() every time.
Yes, I can see why this isn't a good idea -- I'm pained by it myself. I'm just wondering what I might have overlooked among the possibilities available for this kind of code.
If you are writing numerical stuff, you should be using more explicit types such as std::uint32_t.
Unfortunately this can be problematic if you include any library headers that use old-style-casts.
Using -isystem rather than -I makes gcc/clang not emit warnings for headers found in those paths, which does extend to -Werror.
Thanks for the tip.
and -Wconversion and -Wsign-conversion coupled with the new C++11 {braced} initialisers deal with a lot of bad implicit conversions inherited from C
Very interesting article http://research.swtch.com/gorace, but it's very old, published in 2010. First Golang stable release r56 in 2011/03/16 https://golang.org/doc/devel/release.html.
> Go provides automatic garbage collection of allocated memory. It is not necessary (or possible) to release memory explicitly. There is no need to worry about heap-allocated vs. stack-allocated storage, new vs. malloc, or delete vs. delete[] vs. free. There is no need to separately manage std::unique_ptr, std::shared_ptr, std::weak_ptr, std::auto_ptr, and ordinary, "dumb" pointers. Go's run-time system handles all of that error-prone code on the programmer's behalf.
This is not an attractive statement to C++ programmers like the author probably imagines it is. We like being in control of our memory. C++ programmers who would prefer to not manage memory have probably already switched to Java or C#.
This is not an attractive statement to C++ programmers like the author probably imagines it is. We like being in control of our memory. C++ programmers who would prefer to not manage memory have probably already switched to Java or C#.
Ian Lance Taylor wrote the article; he's one of the best C++ programmers I know. So I imagine he is speaking to _some_ subset of C++ programmers, just maybe not the one you belong to.
[deleted]
So, when writing a high-performance, scalable web-server, which language should I pick: Go, C++, Rust, Scala, other?
Whichever you're most competent in. Any of those languages (and many others) would be a suitable option, so it boils down to what you are most proficient in.
I would amend that. Start with whichever you're fastest (not necessarily most competent) in. Once you discover whether the whole thing is worth the effort, move towards safe languages. That's basically Rust, Scala, or (functional-language-of-your-choice-or-) Haskell.
EDIT: Personally, I would start in something like Haskell or Scala just to do a proof-of-concept without having to worry too much about undefined behavior (C/C++) or absurd verbosity (Java), but then I am pretty familiar with those two languages. Maybe it would be worth learning one or two very terse languages to start with, just in case you hit upon a great idea? (Btw, I think Python or Perl qualifies.)
EDIT#2: As a sort of second or third order bit of advice, I'd encourage anyone to brush up on programming languages that might make you faster. It's a sort of "propellant" and can increase your speed exponentially.
EDIT: Personally, I would start in something like Haskell or Scala just to do a proof-of-concept without having to worry too much about undefined behavior (C/C++) or absurd verbosity (Java), but then I am pretty familiar with those two languages. Maybe it would be worth learning one or two very terse languages to start with, just in case you hit upon a great idea? (Btw, I think Python or Perl qualifies.)
EDIT#2: As a sort of second or third order bit of advice, I'd encourage anyone to brush up on programming languages that might make you faster. It's a sort of "propellant" and can increase your speed exponentially.
Rust isn't ready yet so I wouldn't pick that.
Scala I'm not familiar with.
C++ is great. Very fast and powerful, and really not as complicated or difficult as people make out. The manual memory management thing isn't really an issue if you do it right. However writing multithreaded C++ code is never fun.
Go is very simple and easy. I think of it like Simple English. It's very easy to write, and everyone will understand what you are saying, but you'll end up being way more verbose and often a bit more clunky than you might be in a more powerful language like C++. Huge limitations are that there is no overloading at all. I.e. you have to implement `minFloat64(a, b float64)`, `minFloat32(a, b float32)`, `minInt(a, b int)` and so on, whereas in C++ function overloading means it is just `min()`, and templates mean you only need to write it ones. The other big issue is lack of templates (AKA generics), so containers (list, map, etc.) are a pain in the arse to use.
However Go does have great concurrency support, and is close enough in speed to C++ that I'd probably always choose it to write servers in.
So I'd say Go, maybe C++. In a year reconsider Rust.
Scala I'm not familiar with.
C++ is great. Very fast and powerful, and really not as complicated or difficult as people make out. The manual memory management thing isn't really an issue if you do it right. However writing multithreaded C++ code is never fun.
Go is very simple and easy. I think of it like Simple English. It's very easy to write, and everyone will understand what you are saying, but you'll end up being way more verbose and often a bit more clunky than you might be in a more powerful language like C++. Huge limitations are that there is no overloading at all. I.e. you have to implement `minFloat64(a, b float64)`, `minFloat32(a, b float32)`, `minInt(a, b int)` and so on, whereas in C++ function overloading means it is just `min()`, and templates mean you only need to write it ones. The other big issue is lack of templates (AKA generics), so containers (list, map, etc.) are a pain in the arse to use.
However Go does have great concurrency support, and is close enough in speed to C++ that I'd probably always choose it to write servers in.
So I'd say Go, maybe C++. In a year reconsider Rust.
Having done web servers in Java, C++, Ruby, and Python, I was pleasantly surprised by the capabilities offered by Go. In these other languages, the first thing you do is usually figure out a framework to use, which usually comes with its opinionated way of doing things. Often, the framework is really there to paper over issues with the language or the available standard libraries.
In go, the std library offers very good support, and if you need more, there are things such as gorilla (a web toolkit that enhances the std lib). There are also frameworks, of course, but many people find there is much less of a need for that.
The goroutine support is really great, and servicing concurrent client requests with them is effortless.
In go, the std library offers very good support, and if you need more, there are things such as gorilla (a web toolkit that enhances the std lib). There are also frameworks, of course, but many people find there is much less of a need for that.
The goroutine support is really great, and servicing concurrent client requests with them is effortless.
If you choose Go, you get simple scalable language with safe memory, superior standard library and performance close to C.
Performance: Go vs Python - http://benchmarksgame.alioth.debian.org/u64q/compare.php?lan... Go vs C++ - http://benchmarksgame.alioth.debian.org/u64q/compare.php?lan...
Performance: Go vs Python - http://benchmarksgame.alioth.debian.org/u64q/compare.php?lan... Go vs C++ - http://benchmarksgame.alioth.debian.org/u64q/compare.php?lan...
I believe you're going to learn a new language for future problems, and I strongly recommend Rust.
If your performance (throughput and latency) requirement is not very critical, language really doesn't matter. Even scripting languages like Python, Javascript or Ruby performs very well by spawning servers on each cores.
But if your performance requirements goes serious, I believe you will get unusual bottleneck (e.g. GC, VM, memory usage pattern, specific hardware,... ), then you'll want a kind of "full-control". In this case, the only traditional choice was C/C++, and that's why many large-scale companies like Google and Facebook are using C++ internally.
Anyway C/C++ cannot provide enough level of safety that required to provide good productivity. As a workaround, you can use a sort of dynamic checkers (sanitisers), but the dynamic checkers are very immature, and fundamentally dynamic.
A new, and the only current alternative for this case is Rust. Rust provides far batter safety from first at "compile time". It also provides far better linguistic constructs and semantics. But the language itself is immature.
Anyway, (1) there is huge demand for this kind of features from C++ community and (2) Rust is completely engineer community driven (3) and fully open-sourced. So I expect the maturing speed of the language will be incredible that never been existed in language history. And it's fully open sourced.
If your performance (throughput and latency) requirement is not very critical, language really doesn't matter. Even scripting languages like Python, Javascript or Ruby performs very well by spawning servers on each cores.
But if your performance requirements goes serious, I believe you will get unusual bottleneck (e.g. GC, VM, memory usage pattern, specific hardware,... ), then you'll want a kind of "full-control". In this case, the only traditional choice was C/C++, and that's why many large-scale companies like Google and Facebook are using C++ internally.
Anyway C/C++ cannot provide enough level of safety that required to provide good productivity. As a workaround, you can use a sort of dynamic checkers (sanitisers), but the dynamic checkers are very immature, and fundamentally dynamic.
A new, and the only current alternative for this case is Rust. Rust provides far batter safety from first at "compile time". It also provides far better linguistic constructs and semantics. But the language itself is immature.
Anyway, (1) there is huge demand for this kind of features from C++ community and (2) Rust is completely engineer community driven (3) and fully open-sourced. So I expect the maturing speed of the language will be incredible that never been existed in language history. And it's fully open sourced.
Go will never match (in performance) C or C++ (using epoll) for this task.
You're right, but it's got nothing to do with not using epoll.
Still, Go is far faster than what most people use on the web.
https://www.techempower.com/benchmarks/
Still, Go is far faster than what most people use on the web.
https://www.techempower.com/benchmarks/
The same test but on i7 https://www.techempower.com/benchmarks/#section=data-r9&hw=i... - Go winner!
Clojure should also be a consideration.
You would pick Java
Go for C++ programmers, or.. trying to keep a straight face when explaining that no, just implementing "Less" to sort an array of some type isn't enough. You'll also need to tell Go how to get the length of an array and how to swap two elements.
Now, don't get me wrong. I actually like Go for the things I use it for: replacing Python.
Now, don't get me wrong. I actually like Go for the things I use it for: replacing Python.
Python outcompetes C++ in the market by a significant multiple. Python is used in diverse applications, many of them quite ambitious --- game backends, computer algebra, cryptography, network filesystems, simulation code, machine learning.
So is it just that the subset of applications that still get written, for some reason, in 2015 C++ are heavily dependent on specialized Sort implementations? Or is it instead the case that really Sort just isn't that big a deal?
It's pretty easy to look around briefly and see that a fair amount of ambitious stuff is being built in Golang. Somehow, they manage to do it without sum types and generics!
So is it just that the subset of applications that still get written, for some reason, in 2015 C++ are heavily dependent on specialized Sort implementations? Or is it instead the case that really Sort just isn't that big a deal?
It's pretty easy to look around briefly and see that a fair amount of ambitious stuff is being built in Golang. Somehow, they manage to do it without sum types and generics!
I'm not entirely sure what argument you think I'm making or what you're arguing against. Yes, Python is used a lot. Go is used a lot. C++ is used a lot.
Programming language design is all about tradeoffs. The makers of Go went for simplicity. There are pros and cons about their approach. What do you do when you run into something like this? You put your head down and work through it, like I have done every time I've had to write those three functions for sorting something in Go.
We can talk about the cons of an approach without having to point out that people work through it. After all, it's pretty easy to look around and see that a lot of ambitious stuff is being built in C. Somehow, they manage to do it without memory safety and generics.
Programming language design is all about tradeoffs. The makers of Go went for simplicity. There are pros and cons about their approach. What do you do when you run into something like this? You put your head down and work through it, like I have done every time I've had to write those three functions for sorting something in Go.
We can talk about the cons of an approach without having to point out that people work through it. After all, it's pretty easy to look around and see that a lot of ambitious stuff is being built in C. Somehow, they manage to do it without memory safety and generics.
Just try to remove all C++ applications from your everyday life, you will have the answer to your question.
While true, and saying this as someone that likes C++, the reason many of those applications are written in C++ is that the choice of imperative languages with AOT compilers is not that big.
I remember having to fight to use this new strange thing called C++.
I remember having to fight to use this new strange thing called C++.
It's pretty easy to look around briefly and see that a fair amount of ambitious stuff is being built in Assembly. Somehow, they manage to do it without structured programming and high level languages!
No, it's not easy to look around and see that, because that isn't happening.
It was happening in the 60-90's time-frame.
So people were productive without higher level abstractions, but then they moved on.
Likewise, people were once productive using languages without generics during 60 - 2004(Oberon, C, Java < 1.5, Turbo Pascal, Modula-2), but then they moved on.
So people were productive without higher level abstractions, but then they moved on.
Likewise, people were once productive using languages without generics during 60 - 2004(Oberon, C, Java < 1.5, Turbo Pascal, Modula-2), but then they moved on.
Well go has a nice grammar. Seems like you won't have to write much boilerplate if when you can automate writing the code.
What bugs me most is this popularity contest. For example, the idea that go will go mainstream and writers of the future will write about people that did keep a straight face when explaining what you said. You know how people unwittingly revise history. This field has too much fashion going on and not enough engineering.
What bugs me most is this popularity contest. For example, the idea that go will go mainstream and writers of the future will write about people that did keep a straight face when explaining what you said. You know how people unwittingly revise history. This field has too much fashion going on and not enough engineering.
And it also doesn't encourage long compile times, so you might be tempted to do less thinking and more compiling.
As a C++ programmer, if I wanted that kind of stuff why wouldn't I switch to java.
You get the ability to use lightweight concurrency contexts (go-routines) + channels. Those are built in. The included library is very good.
But if you are used to generic programming and you think in terms of it, type handling in Go and might think it is a step backwards and Java might be close to what you'd want.
Even better, I think as C++, Rust is starting to look interesting. I am waiting for them stabilize it more (maybe get to beta 1.0 status), because I think it has some very cool things going for it.
But if you are used to generic programming and you think in terms of it, type handling in Go and might think it is a step backwards and Java might be close to what you'd want.
Even better, I think as C++, Rust is starting to look interesting. I am waiting for them stabilize it more (maybe get to beta 1.0 status), because I think it has some very cool things going for it.
Go is less verbose than Java arguably. Multi-threadedness in Go is very straightforward. Native code binaries can be beneficial in some cases.
For me, the killer feature of Go is that it produces executables that are quick to start up and run with a small memory footprint. It is great for utilities or agents or small daemons; where startup time or memory size are critical. It can also run without a JRE or other dependencies, which is nice if you are distributing software to end-users.
So I'm not sure I agree on your first two points. For me, lack of exceptions and generics means Go code is at least as verbose as Java, if not more so. Multi-threadedness is a wash for me; channels vs fork-join and executors are just not that different.
I think Java can produce native code today, but it isn't worth it for most scenarios where you would actually use Java. I think we'll see that change if Oracle ships an AOT compiler in "official Java".
So I'm not sure I agree on your first two points. For me, lack of exceptions and generics means Go code is at least as verbose as Java, if not more so. Multi-threadedness is a wash for me; channels vs fork-join and executors are just not that different.
I think Java can produce native code today, but it isn't worth it for most scenarios where you would actually use Java. I think we'll see that change if Oracle ships an AOT compiler in "official Java".
Coming from a C++ heavy background, I do miss generics. But I have been trying to be cautious of bringing my C++ habits to Go, opting instead to embrace the alternate model Go brings to the table. spf13 goes over some of these points in his talk here: http://vimeo.com/115776445
I do not use exceptions in C++ so that was not a big change for me. Our code needs to be relatively deterministic (even though we are not hard real-time yet) and exceptions hamper with that goal.
I incorrectly used the word "multi-threadedness". Concurrency in go is very straightforward. Channels are extremely lightweight and are different from forking (which creates a new process with its own address-space).
I do not use exceptions in C++ so that was not a big change for me. Our code needs to be relatively deterministic (even though we are not hard real-time yet) and exceptions hamper with that goal.
I incorrectly used the word "multi-threadedness". Concurrency in go is very straightforward. Channels are extremely lightweight and are different from forking (which creates a new process with its own address-space).
I watched the entire video which was interesting, but I noticed that he is performing read operations on a map without any kind of read lock. That is not safe behaviour I am pretty sure :)
AOT compilers are available in many licensed JVMs, but one is planned for official Java around Java 10 time-frame.
http://cr.openjdk.java.net/~jrose/pres/201502-JVMChallenges....
Slide 12.
http://cr.openjdk.java.net/~jrose/pres/201502-JVMChallenges....
Slide 12.
That would be amazing if Java didn't have AOT compilers. (See my other comment.)
> Native code binaries can be beneficial in some cases.
Available in Aonix, IBM J9, Jamaica JVM, Excelsior JET
Just because OpenJDK doesn't offer it, doesn't mean others don't.
Available in Aonix, IBM J9, Jamaica JVM, Excelsior JET
Just because OpenJDK doesn't offer it, doesn't mean others don't.
That's only true for single-thread Go. Go is not memory-safe in multithread mode, because exploits using race conditions are known.[1] This, incidentally, is why Go is locked down to one thread when running under Google's AppEngine.
Go is a good language for server-side web work - all the expected libraries are there and in good shape. That makes sense; that's why Google had Go created. Go is a good option when Python and Javascript/node.js are too slow. But Go's concurrency isn't as clean as its enthusiasts claim.
I'd recommend Go for programmers coming from the Javascript/Python/Perl world. Although Go is a hard-compiled language, it's surprisingly similar to the scripting languages when writing routine server-side code. C++ programmers will find Go easy, mainly because it's garbage collected.
(The replacement for C++ will, if we're lucky, be Rust. Rust has roughly the complexity level, headaches, and gotchas of C++, but it catches all the memory-related bugs at compile time. Rust isn't ready for production use yet, though. Give it a year.)
[1] http://research.swtch.com/gorace