Programming frameworks can be a huge boon in terms of simplifying the provision on desirable features and behavior that aren't the core of your project.1 But they evolve toward someone's ideal of an all-singing, all-dancing, universal tool and that makes them huge and complicated. For a lot of the things I write at home, I don't need most of those facilities and I don't want huge or complicated. But I also don't want to be writing thousands of lines of code just to make my tool ergonomic.
Thankfully the open source community provides hundreds of high quality utility libraries covering all kinds of tasks you might want to do. Usually and handful or more for every feature you might want. Which means you have to choose.
This week, I decided that I wanted a command-line argument processor for a tool I"m dinking on. I've used gengetopt in the past when simple ad hoceries were insufficient, but it needs a two stage build and has a clunky, pure-C interface. Surely I can do better than that!
How to decide?
Well, I have a partial answer: The build system I use for home projects is Meson, which uses a facility it calls "wraps" to support fetching and building projects you depend on.2 Moreover, the tool has a facility to fetching wrap files from a centralized database which makes adding a provisioning library to your project nearly transparent.
So I'm going to restrict my consideration to tools that appear on Awesome C++ and are available from Meson's Wrap DB. There might be other good options out there, but these have had some level of peer review and (critically) are easy for me to get. That gets me down to:
After a cursory reading of the github READMEs it's clear that they all provide the features I need and have a version of the same kind of interface that QT uses.3
But that doesn't mean they're equivalent. One (argparse) uses a Method
Chaining (AKA Fluent) interface for the "tell the parse what arguments to
expect" step,4 and another (cxxopts) uses method chaining with
statically polymorphic calls to operator()
which makes a slightly
weird syntax that I suspect clang-format is going to balk on. Neither of those
is a deal breaker, but they are a little odd. Alas, the third option (CLI11) is
a little clunky: requiring you to create two objects and relying on a try-catch
for detecting errors (though it provides a c-preprocessor macro to hide the
exception handling if you prefer).
Anyone have any relevant experience here?
1They also provide consistency across a set of tools for both users and developers, and you should not neglect that value in that. At work we use Qt, which has all the faults I list above and a swath of legacy issues as well. But the bit where my manager can say "Hey, we like that command line feature you added on your project, put it in Jane's too." And even though I've never worked in Jane's project before I can still dive right in because I know how command-line interactions work in QT. Programming at scale is a team sport and that changes the trade-offs.
2 I already use the doctest wrap in several projects, so I know how this goes.
3 You create a parser object, tell it what arguments you expect, pass it the command line, and then ask it what it found. With convenience facilities for detecting violated expectations issuing help messages, and so on.
4 I've tried method chaining a few times in my own coding, and found that I only like it in cases where most of the uses of a class are going to be transient. Thankfully, if you declare a named object you don't have to use the fluent syntax if you don't want it.
No comments:
Post a Comment