Hacker News new | past | comments | ask | show | jobs | submit login
Qt binding for Go with support for all major operating systems (github.com/therecipe)
324 points by c8g on Nov 11, 2016 | hide | past | favorite | 134 comments



I love that the author provides docker images for cross compilation. Cross compilation environments are such a pain to setup and maintain - having docker images with all that stuff in is a major step forward.

Next time I want to build a desktop app I'll have a go with this for definite.


author here; Using Docker saved me from creating the code to cross compile from windows->linux, darwin->linux/windows, ... And this alone was worth it, but being able to ship something that works out of the box is nice a nice bonus :) edit: I'm also planning to create macOS and iOS target images.


Docker will only let you cross-compile to OSes with the same kernel, which is relatively limited. A full set of say vagrant files would be slower, but a lot more polyvalent (but even then it would not let you, say, compile a windows binary from linux).


You can cross-compile to completely different environments. It just can be tricky to set up, so prepared docker images with e.g. the toolchain to build a Windows executable on Linux are great to have, and what is provided here.


I think it's a nice idea in theory to provide cross-compilation support by means of emulating all possible target architectures on the compiling machine.

Then I remember that I'd be managing a custom heterogenous compute cluster just to compile some code.


It's not emulation, it's cross-compilation which has existed basically forever. You don't have to target the same instruction set as the one you're compiling on.


I am well aware of that, I was referencing the concept of "cross-compilation" the GP seemed to have.


I see what you mean now, using like qemu or something. So "emulated native compilation". It is an interesting concept. Sorry for misunderstanding you.


Indeed, "virtualbox-the-crosscompiler" (probably from hell). QEMU would be a few orders of magnitude slower but I think it supports many more archs.

No hard feelings :)


As long as they provide the correct kernel headers and library headers (libc etc) for your target, it should work fine in docker.


Go doesn't really use libc all that much. Syscalls are also implemented in pure go + as, so there aren't really header files to provide. That makes for a great cross-compilation story.


I wish the author success. Many UI libraries for Go have been made and abandoned. It would be a joy to see one succeed.


Not only a joy, it would be the best way we currently have to make desktop applications. Currently the easiest alternative is JS+Electron, but that's just horrible because of all the waste of resources. Python is hard to package, so something statically compiled would be great.

I would prefer Rust for this (or, well, I would prefer Python with a better packaging story), but Go is much better than the alternatives too.


While not very modern, Lazarus (http://www.lazarus-ide.org/) is a Delphi-compatible Pascal implementation for cross-platform GUI apps. While I've not coded in it I've used a couple of Lazarus apps, and it seems quite mature and works well.


python can be packaged up. I haven't done it, but there are some popular 100% python apps with a UI that dint require any global python crap.



Like Sublime.


Sublime Text is mostly written in C++, Python is only used for plugin API.


was thinking this also. The language seems very well suited to GUI applications based on its internals and I would love to see what comes of it in this regard. moar bindings pls


author here; Thank you :)


Serious question:

Once I have been building Qt Widgets application in Python with PySide bindings. I needed to do some simple processing in several SLOTs and decided to use lambdas as an easy replacement. Kids, do not even think about attempting to do this at home, seriously.

I have run into some extremely evasive random crashes and stuff. I did not dig to the bottom of the issue, although my guess is that Python lambdas are executed in main (constructing) thread context and that causes race conditions.

My question: how do Golang's goroutines interact with native threads spawned by Qt?


Usually these kinds of errors are due to weird interaction of the python GC and the parent/child lifecycle used internally by QObject (and derived classes).

In this case, you're depending on the Qt side (signal/slot management) to keep your lambda alive along with its entire call stack. But python doesn't know anything about QObject pointers, and will happily GC it. The next time that signal is fired, crash is extremely likely.

This used to be a much bigger problem in PyQt and PySide 4 and earlier, but it appears to have gotten better recently in PyQt at least.


Yeah, I recently found some sample code written with a weird (non-standard) mechanism of moving data between Python and Qt and it had all sorts of problems. For example, they had a Python thread that was running a timer and when Qt shut down, it tried to deliver a Python callback afterwards and things got messed up.

In general, the best way to work with Qt is to let it assume control, and it use its mechanisms for lifecycle-management. I converted the code so that it used a QThread-derived Python class and QTimer. Works great.

You basically have to understand the Deep Magic that makes Python work under the hood, and ditto for Qt, too.


author here; The main golang thread lives in the same thread as Qt's main event loop. If you now want to "cross" threads from a goroutines (which may lives in another thread), you can use Qt's signals and slots to safely "cross" threads. Take a look at the "quick/bridge" examples to see how Go is interacting with QML, and therefore has to "cross" threads several time.

edit: can't comment any further currently, I will try to answer everything once I'm unblocked.


Ugh, sorry about that. New accounts are rate limited to try to cut down on spammers and trolls. It's really bad that this also cuts down on participation by the creator of a project showing up to comment on it, which is one of the best things that can happen here.

We've marked your account legit so it won't happen again. Welcome to HN!


Thank you very much :)


I have some experience building PyQt apps and can confirm random crashes (segfaults, sigaborts and such). I suspect this has nothing to do with lambdas. It may likely be something happening between Python and QT - remember that all things happening in QT/C++ world are invisible to Python. For example if your Python routine returns None (as many Python methods, functions do) instead of some other object that is expected by C++ (say QNetworkRequest) it may crash and die without any traceback. I spend hours on things like this - QT method must return something but in some cases was not returning expected object and whole thing was crashing. Python being Python doesn't care about return types, there is no Python traceback, you're left in the dark groping, browsing codebase trying to find what might get wrong, no debugger will help you.

Working with QT bindings is very difficult. Debuging things is not easy. I'm not sure if using bindings is worth it. I suspect doing things in plain C++ QT might be easier to debug and maintain.


In C++ one gets a compilation error if the wrong type is returned from a function.

Qt is generally type safe, but it uses quite a lot of pointer types and also a rather unusual for C++ parent-child resource cleanup rule for QObjects (e.g. all widgets).

When something does crash, C++ doesn't offer any tracebacks, but if one has a core dump or can reproduce the crash under a debugger, a traceback can be easily obtained in the majority of cases.


> but if one has a core dump or can reproduce the crash under a debugger, a traceback can be easily obtained in the majority of cases.

how do you create core dump? starting program under some debugger? I tried debugging with valgrind but the output contained so much noise it took my ages to find something. I also had to recompile QT used by PyQT because QT because QT requires some flags to be able to debug. What's the best way to debug things like this?


A core dump is a file that's generated by the OS and includes a snapshot of the process state. It's useful when e.g. one is not able to run the program under a debugger. Enabling it depends on the OS, I would just search for enable core dumps on "my OS".

If one can run the target program under a debugger, a core dump is not so helpful because it's a static snapshot, whereas a debugger can modify program state.

I am guessing that one has to run the python process itself under the debugger and pass the parameters such as the script path.

There will probably be a signal or exception condition at one point and one can take a look at the call stack. The call stack will probably be messy, as it will include python internals.

An alternative way to debug this is to understand the lifetimes and preconditions of C++ widgets and signals and slots and review one's python code.

Unless there's a bug in Qt itself, PyQt and Qt might have a misunderstanding about lifetimes and something gets cleaned up too soon or there's a dangling refrence to a cleaned up object


There is a gdb plugin for Python. I'm not sure how other distros handle it, but on Arch Linux you have to build Python yourself for it to work.


It should create a coredump on crash by default (either in the current directory or in a OS specific system directory, try /var/cores for example). If it doesn't, try 'ulimit -c unlimited' in your shell to enable coredumps for that session.

Valgrind is not really useful for debugging crashes. Running under gdb would work though.


IMHO Valgrind is immensely useful for debugging crashes.


I hit some crashes with PyQt which were fixed if the Python slots were defined with "@slot". I think this may be now fixed.



This is a very interesting question (to which I don't know the answer), I'm curious how the coroutines will interact with the Qt events.

Sidenote: It's also possible to build Qt applications in Python using AsyncIO (using quamash), by integrating the Qt event loop as an AsyncIO event loop. This can be helpful to avoid race conditions, as much more interactions can be handled without thread concurrency.


Thanks to quamash you can use the asyncio event loop to build a Qt app that call other async stuff at the same time !

I managed to use pyqt and autobahn|python (wamp protocol, RPC and pub/sub) in the same app thanks to quamash.

I set up an example app here https://github.com/icefo/PyQt5-Wamp


I've run into this problem too. I think it has more to do with object lifetimes. Suppose you define your lambda in a method of a QWidget that's later destroyed. The underlying C++ object has been cleaned up already, but the signal's reference to the lambda is keeping the Python part of the QWidget alive. Then when the slot is triggered you get weird crashy behavior.


IIRC this occurs when Qt keeps a reference to something but Python deletes the object; and thus segfault.

I've fixed crashes by keeping stuff assigned to variables to keep it from being GCed.


So this is more comprehensive than the qml binding library?[0]

[0]https://github.com/go-qml/qml


The Author said about go-qml and this binding in reddit[0]

[0]https://m.reddit.com/r/golang/comments/585rvs/comment/d8xtre...


go-qml/qml is just a tiny wrapper around the QML API, which is nothing compared to this. This library covers the whole entire Qt framework (Qt Widgets libraries + QML + everything else).


It for sure is. However QML might also be all you are interested in, since Go already has a very good standard library which covers lots of things. Then the remaining question is which of those Go QML wrappers is of a higher quality and easier to work with.



Now if only someone did the same thing for .NET Core. Ideally someone who would want to build a business on that :)


You mean Go bindings for .NET Core?


.NET Core bindings for Qt. .NET Core doesn't have any UI toolkit right now. There are several efforts but they're mostly hobbyist ones and seem to lack funding or major development power, unfortunately.


I had a Windows Forms UI running on my Mac 6ish months ago (when I was kicking the tires on .Net core).


I would rather use XAML than JavaScript dialects


What about WPF?


It's not designed to do cross platform UIs and QT already is. It's be easier to bind QT to .NET than make WPF cross platform.


Requires DirectX, so it won't run outside Windows.


Does this library support making "derived classes" from QObject/QWidget? And how does that work, in Go- are they interfaces?


author here; Yes, you can create derived classes from any class that is derived from QObject. Simple anonymously subclass in Go and add your own slots/signals, as shown in the "quick/bridge" and several other examples. Then run "qtmoc" to generate the need c++ and go code. All functions usually accept interfaces and all classes have their corresponding interface, which itself inherits all the derived interfaces.


That's great!


Could someone please enlighten me why an installation would need 8gb of free ram?


If it's building everything (including Qt), Qt includes Webkit. I imagine that's a lot of C++ code to build.


QtWebkit and QWebEngine, which uses Blink(/CEF).

8GB RAM is rather optimistic.


author here; It's needed by `go install` to provide pre-compiled packages, which will then speedup the compilation of your applications down to 10sec. But you can also use the STUB setting, before running the setup and then use the binding with only 1gb ram as well. (the necessary c++ code will be compiled later with qtdeploy)

edit: can't comment any further currently, I will try to answer everything once I'm unblocked.


I am wondering if there is something similar for Rust. Does anyone have some information on this?


Not Qt, but I'm using https://github.com/gtk-rs/ with Rust, which is pretty cool, I've developed the GUI using Glade, which I load via Rust. I'm also using CSS, to style the interface.


Unfortunately GTK itself is pretty bad, and it constantly has breaking changes. Hopefully things are improving.


https://github.com/cyndis/qmlrs is not complete and stalled but usable. It uses Rust macros instead of moc.


Question. Does QT still have a licensing problem for closed-source apps on mobile platforms?

Last I saw, QT's GPL/LGPL license required you to open-source the code that was statically linked to it. That wasn't much of a problem for desktop platforms, as you could just package up all of QT into its own QT.DLL (or the equivalent) and link to it dynamically. But this isn't allowed on stuff like iOS, so you were required to buy a license for those.


Only parts (non-core) of QT are GPL, most are LGPL

And it's a bit of a misconception that LGPL requires statically linked things to be open sourced: in that area it only requires that the LGPL components be replaceable (and the source for the LGPL components be available). In the context of static linking, that can be done by releasing pre-link binaries (object files) with instructions for linking them.

Of course, that's likely more work than just making the source available under a proprietary license (ie: provide source code so LGPL components can be replaced).

If you're using Qt GPL components, then yes, you'll need to open source your application (or pay for a Qt License)


author here; IANAL but I think for iOS it's still required to either open-source your code under GPL or to buy a commercial license as you said. But IIRC there is work being done to bring dynamic linking to iOS as well, since apple allowed it with iOS 8 or so.

edit: can't comment any further currently, I will try to answer everything once I'm unblocked.


That's an iOS (and Android) issue, not a QT issue.


Qt itself is LGPL now, so it no longer requires you to open-source code linked to it.

Some of the language bindings that aren't maintained by Digia are still GPL, though. An example of this is PyQt, and the PyQt team's intransigence on the GPL is why PySide exists.


nitpick: its Qt, not QT.

QT is Apple QuickTime which some consider as the worst Windows software ever created.


Adding to that, "Qt" is pronounced like "cute", while "QT" is pronounced like "cutie" (though with different stress).

I was guilty of mispronouncing it too, until Qt 4 came out and they released that goofy "Qt 4 dance" video.


Why do the iOS screenshots look nothing like iOS? https://github.com/therecipe/qt/blob/master/internal/screens...


It's the Qt framework, not iOS's UI framework.


It definitely looks like Qt but I am surprised that Qt is not using any native controls on iOS. It tries to use native controls or at least a native look and feel if possible (it does so on Windows and macOS at least). On the other hand, I don't know anything about mobile app development and apis that iOS gives you so I cannot tell whether such a thing is possible in iOS at all.


Two points:

a) Qt doesn't use native controls on any platforms (though for half of desktop Linuxes Qt is the native toolkit). Qt has its own controls that are styled to look like native.

b) The screenshot shows Qt widgets. This is not the intended way to do Qt on mobile platforms, and thus no native styling is provided. QML is the official way to do mobile apps with Qt.


Point A is interesting because you can see particularly on OSX that it is not native, despite widespread recommendations to use Qt for cross platform development. I have used wxWidgets on multiple platforms and despite its idiosyncrasies, I have found it performed very well and is obviously truly native on all platforms (unless you choose wxUniversal as a rendered; nobody does) or unless you do "owner drawn" stuff and paint yourself. In any case I would not hesitate recommending wxWidgets for native controls over Qt.

Other minor Qt annoyances include their own scrollbars which ignore the system setting of "jump to where I click the thumb" setting etc.


Hmm. Entirely possible certain APIs aren't available in a certain way. I wonder if it's possible to "skin" it, in a fashion.


Might just be that they didn't do that work yet but it is trivial and just requires some skinning that hasn't been done yet?


author here; Because I used the C++ widgets, which are not intended to be used on mobile platforms. The proper way to develop mobile apps is in QML/Qt Quick, and those would look great. You can style them any way you want, and there are also material (google) and universal (microsoft) themes that can be easily plugged in.


Oh, okay. That makes sense. Thanks for the reply!


I've been using this library for a couple of weeks, and so far I have been very impressed. It's relatively easy to use, and seems to be pretty comprehensive in the amount of the Qt API that it supports.

I raised two small issues, and both were addressed quickly by the author. In one case incredibly quickly.


Sadly, FreeBSD is not included in the supported OS list.


author here; Didn't know it was supported by Qt, I will put it on my TODO list ;)

edit: can't comment any further currently, I will try to answer everything once I'm unblocked.


This is awesome. I was really bummed about the state of QT support in golang. I played with the go-qml project, but it wasn;t everything I needed. This looks amazing.


Does this support QML/Qt Quick Controls 2?


author here; Yes, as well as material design and all the stuff that comes with QML/Qt Quick.


Not sure if this does or not, but this project does: https://github.com/go-qml/qml/


Please, please where is a proper C# binding?


big fan of the repo http://github.com/dontpanic92/wxGo/ it's got everything Qt has but free


Is Qt not free?



I don't know as what you define free. But Qt is as far as I know is available under LGPL, GPL and under a commercial license. Also they co-operate with the KDE project and made sure that it will stay "free" IIRC.


Is there an example of using data from Go in the application?


author here; Take a look inside the `internal/examples` subfolder. There are several examples.

edit: can't comment any further currently, I will try to answer everything once I'm unblocked.


Does anybody know why the demo application is 52mb?!?

I mean, yes, its statically linked but 52 million bytes for a window, a button and a pop-up?

Shouldn't this fit into 5-10 mb? Is the whole qt lib linked into that binary?


author here; The size depends on your platform and the way you build it. If you used `qtdeploy` instead of `go run/build`, you should get a 2mb binary which includes Go's runtime + only the needed Qt C++ code. (A normal go binary has the size of 1.5mb or so) The rest are Qt dynamic libs, which are not all needed, depending on what you build. You can safely remove plugins and maybe whole modules. edit: and you can also compile Qt on your own to strip out ICU and other unnecessary stuff + with Qt 5.8 things will get even more lightweight.


First of all, thank you. The effort of maintaining Qt in any way or form is ... nontrivial.

I've wanted to try this one out, but it looks like I'm maybe trying something too far off from mainstream. I like how the bindings can be built against incomplete Qt releases (iow. just the development packages available in debian sid); the build complains when some modules can't be found but continues nonetheless. Sure, it takes QT_DIR and PKG_CONFIG settings to build like this but I have no problem there.

So far I haven't found a way to build even the sample project against this type of custom build though. I'll try to find more time to dive in as to why this happens but I'm sure this is an error on my end. Once I figure that out, I should be able to provide a documentation update.

As to why? I have an old project which I need to revive, and being stuck in ancient GTK land with dubious bindings and even more dubious upstream practices sounds less appealing than porting things over.


Did you tried to use QT_MISC_DIR (which should contains the "mkspecs" subfolder) and QT_DOC_DIR, which should contain for example (QtCore/qtcore.index)? Or if you like, open an issue on github and we can try to sort this out :) edit: also if you use PKG_CONFIG=true, the QT_DIR will be ignored


Hum, I hadn't, but in retrospect I should have. I've done my share of Qt builds in the past and mkspecs path was often an amusing detail. Didn't solve the problem though.

But I think I found out at least one thing that goes wrong. I'll provide a github issue report later this weekend but here's the short version:

* The generated #cgo directives for CXXFLAGS are different for desktop and minimal files. Minimal is generated with -I$(QT_INCLUDE_DIR)/QtFoo include paths; desktop is generated with -I$(QT_DIR)/<major.minor>/gcc_64/include/QtFoo paths instead. So headers in desktop build are looked up from library directory paths (and with obviously bad path elements too).

* When building, qtsetup goes for desktop target.

More data coming in via github once I get the exact details sorted out.


Mh, usually the minimal_cgo_* files and the cgo_* files should use the same imports.

But the qtsetup won't always re-generate these cgo_* files if the QT_DIR is up to date (there is probably a bug if you use QT_PKG_CONFIG).

So I would use `qtminimal` or `qtdeploy` for debugging this, because the minimal_cgo_* files are always re-generate.

And you may also want to change `InfoLevel` in `internal/utils/logger.go` to `DebugLevel` and then re-build the cmd/tools to get additional infos.

Hope this helps :)


hehe, thx for the quick reply. Had some trouble with qtdeploy and since go build worked (and I prefer go build workflow wise) I assumed it would result in the same binary.

The demo application works without any of the libs from the deploy folder, but thats probably because it uses the system libs.

So cool:

- Option 1: 'go build' creates one large 50mb binary

- Option 2: 'qtdeploy' creates a bundle with a little more than whats required but you can remove what you dont need

- Option 3: just takes the binary and let the system provide the libs

Super cool would be, if that downsizing could be made automatically but since that icu stuff takes alone 24mb, that might be difficult. Anyway, great work. Spend half a day to get it working with system libs, but in the end I had to learn to just follow the tutorial and download qt again.

Great job :-)


Thank you :)

I should probably mention that Option 1 is still depending on all the system/dynamic libs. And therefore only usefull to save time during development or if you need more control about the build. If you want to get a smaller binary, run `qtminimal` and `go build/run -tags=minimal` to include only the necessary C++ code, but the binary will still depend on the dynamic/system libs. I will add full statically linking in the future, but probably only after Qt 5.8 is released.


I think you answered your own question, if it is statically linked to qt then it is statically linked to all/most of it no? I don't think qt or any gui library is divided in many small pieces like javascript libs.


Please, I beg of you, do not create an iOS app using this library. Look at that screenshot: https://github.com/therecipe/qt/blob/master/internal/screens...

It looks like a tiny Windows 95 on your phone. Tap targets way too small.



Honestly speaking, I find "Win95" look and controls much more intuitive and easy to use.


The glorious days before flat ui


Flat can be fine with shadows (though if it has shadows is it flat?) and other hints but yes I think the desktop UI in particular reached it's peak with Win2k, everything since has been different but not better.

These days I use XFCE with http://www.noobslab.com/2016/03/vivacious-colors-gtk-theme-s... and it's nice.


Nope, those look much better. I guess just...don't use the widgets.


I'm fairly sure it's the same widgets, just with a better theme overlaid.


No, it's Qt Quick vs. QWidget. QWidget was created in the pre-mobile days, and while still very useful to make high-quality work horse desktop apps, the mobile platforms are much better served by Qt Quick in terms of theming, animation and touch support.

This fragmentation is frustrating sometimes, but in the end what counts is whether the framework has solutions to your problems, and it usually does. (Meanwhile, Qt Quick is improving on the desktop over time.)


This is why on an application I have done back in the 5.3 days, I decided to throw Qt away and rewrite it using standard C++ for the logic, with Java and C++/CX for the UI.

I was much more productive and I had much better integration with the respective OS APIs.


That's because it is done with Qt Widgets. "Proper" way for mobile is to use Qt Quick Controls, but you have to deal with QML/js crap unfortunately.


QML isn't crap. Its much nicer, IMHO, than HTML and CSS and much more intuitive. The animation and states system makes it trivial to make polished looking UI's.

Granted, I wish I didn't have to use JS with it, but QML itself is fantastic.


Of course, when I say it's crap, I mean compared to C++/Qt Widgets. I wish Quick Controls were available in pure C++.


You can use them from C++, but its definitely not the expected usage and not particularly easy.

I personally much prefer QtQuick to QtWidgets, and really like the QML declarative language as a better HTML/CSS for non-web. But I completely agree with you that I would prefer if I could code all my logic in C++ and not JS.

I've been meaning to play around with two ideas:

1. Construct the QtQick widget scenegraph wholly from C++ (and not using QML/JS at all). Doable, but definitely not intended usecase.

2. Construct the scenegraph using QML, but using it only for setting values (and never any logic) and then manually connecting the signals from the QML-created widgets to C++ slots.


It is not wise to even implement a lot of logic in the JS part of QML, as the JS interpreter there is quite a bit more primitive than something like V8. Your performance will suffer if you attempt to write anything too complicated in JS inside QML.


Oh I know, I've had some complex QML/JS in the past.

Even more so if you're running on iOS where the JS isn't jitted.

It's really easy to write QObjects that are accessible to QML and you can connect your signals/slots with the QML widget ones, so having the logic in C++ isn't hard. It's just not quite as convenient as putting it inline in QML, I guess.


Careful what you wish for. XAML had the whole "no code because we want designers to use it" thing going on and the result was a bazillion of converters with a SNR of <<1.


I don't mind code, just... I'd rather if I had choices beyond JavaScript.


How is the QML standard widget library coming? Last I looked you had to roll your own buttons.


QtQuick Controls 2 is pretty complete wth more controls on the way: http://doc.qt.io/qt-5/qtquickcontrols2-index.html

They're easy to use, themeable and hardware accelerated.


I enjoy working with QML. There are a few issues, but it is not "crap" at all.


IIRC Qt Widgets are stylable as well. I guess default distribution does not provide styles for iOS, so it defaults to the Win95 look. To me Qt Widgets is the most elegant UI API out there.


It just doesn't have themes. You know, you can apply CSS styles and JS animations for Qt objects?


I guess no one provided needed Qt Quick Components?


author here; Yes, those look awful. But only because it's build with Qt's C++ widgets (intended for desktop development) and not with the recommended Qt Quick Controls. I will update the screenshots to something that is build with QML.


Where is the proper useful C# binding....


Does Qt have something like React?


QML does provide an FRP-like methodology, which is not exactly like React but in the same universe of UI techniques.


So, dang, why is my account rate limited then? I’ve had 2 cases before where a project I participated in was presented here, and I showed up to comment, and couldn’t do so due to the rate limiting.

This is not really helpful, and destroys any chance at meaningful discussion.

(Also, last time you told me that rate limiting couldn’t be undone, and I should just write an email – what’s that about now?)


There are other reasons for rate-limiting besides an account being new—if an account has posted a lot of unsubstantive or uncivil comments, for example. I doubt that I told you it couldn't be undone, because it can be undone. What I probably said was that you should email us because it isn't a good idea to go on about it here.

We detached this comment from https://news.ycombinator.com/item?id=12932238 and marked it off-topic.


Please change the title - the long string breaks the HN layout on my phone.


Please change your phone ;)


Please add (2016) to the title.




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: