Posts

Overview

The description below applied when it was first written, and has covered a couple of months of posts.  It is no longer entirely inapplicable, but both projects have now been covered, for points if interest, pretty well up from low-level components up to the calling of main(). I am continuing to add posts, but with more general application, and many will not be restricted to spin-offs of the two original projects, nor even necessarily to C++.

CWEB

After a gap of about two decades(!) I decided to revisit composition in Knuth/Levy's CWEB on a C++ project. First, some technical notes: 1) I was unable to get the ctwill macros to work with my TeX system (despite the fact that it is all part of a single distribution). Admittedly, I didn't try too terribly hard, but it means that my output contains no mini-indexes or other ctwill features. 2) I write code using emacs, and this meant also adjusting to switching between cweb-mode and c++-mode. A number of functions I had written in emacs lisp were not portable directly to the new context, as they depended on file naming conventions. (If I were to take up use of CWEB more heavily, they would not be too difficult to modify - search backwards for @(...>= to determine the current output file name.) 3) My initial inclination was to use CWEB includes to generate one TeX file for the project. I abandoned this both because it ended up causing some size-related problems and because I t...

Considerations on an Optimization

An application – actually, successive pair of applications – is meant to process (in the end) about three hundred or so entries to convert a simple markup into complex LaTeX markup. It also enhances the data with information drawn from a flat-file text database (i.e. literally a set of .txt files in a single directory). This application was originally written in rust, which is not my favourite language, but one worth exercising to a degree. The rust application covered a limited subdomain, and generally avoided unsafe rust code, although in some cases it had to use cell mechanisms which converted compile-time to runtime safety. The initial iteration had the text database as one large file, with some logic allowing for extended seeking through chapters. It soon became apparent, after extending my dataset to about twenty or so items, that this was going to result in an unacceptable runtime — it was taking longer to process than the later TeX processing took, and although in theory this w...

Boundaries

Imagine that you have a well-designed application, possibly using dependency injection. It will almost always have a very thin layer visible in main() - capturing command-line parameters and environment variables, mainly[1], and then a call to an Application class which manages the rest of the initial logic - setting up resources such as logging, doing substantive validation of parameters, and creating and running the objects on the next level down which actually carry out the work of the application. This is simply following the SRP at a high level.

Convergence

This is an opinionated piece. Consider: fn handle_doubling_indicator(text: &str) -> (String, String) {     if text.len() == 7 { match &text[6..] {     "A" => ("First Mass".to_string(), text[0..5].to_string()),     "B" => ("Second Mass".to_string(), text[0..5].to_string()),     "C" =>  ("Third Mass".to_string(), text[0..5].to_string()),     "D" => ("Alternative observance".to_string(), text[0..5].to_string()),     "E" => ("Alternative propers".to_string(), text[0..5].to_string()),             _ => (String::new(), text.to_string()),         }     } else {         (String::new(), text.to_string())     } } and compare: std::pair<std::string, std::string> handle_doubling_indicator(const std::string &text) {   if (text.length() == 7)     {       switch (text[6]...

Small and Large Lambdas

Sometimes lambdas seem like a gift from heaven.  They allow the quick generation of STL algorithms without all the headache of setting up the structure of a unary or binary op class, and the ability to create general closures with a single extra character allows not having to worry about specific capturing of members by reference or pointer for use inside the function.  In C++14 and following the ability to define a stand-alone lambda with a given name allows for extra clarity when a given operation has to be executed several times but with a decidedly local context. (If I'm repeating the test-and-set pattern of  auto setImplementations = [&](std::string_view inVal) {     if (m_implementations.get() != nullptr)       throw std::runtime_error(           "Only one of -C, -d, -D, or -i allowed as type specification");     m_implementations = m_factory.create(inVal, m_names, m_dataRequirements);   }; four ti...

Injection versus Encapsulation

We (justly) hear a lot about dependency injection, and in many cases straightforward dependency injection, whether managed via a tool like Swing or just manually (as you have to do in C++) is clearly the best thing to do.  In most of these clear cases the dependency is public and shared: one common call will not only be economical in terms of code, but will allow the class into which the dependency is being injected to become less tightly coupled to its environment. But there are some interesting edge cases.  Excluding those cases where a strategy is purely private to a class -- that is, it is a resource exactly like one which might be injected, but it is properly an implementation detail  of that class and, if visible outside the class, is so purely to allow it to be tested independently[1] -- we have cases where although the resource is not unique to one class its implementation will be different in some way locally.

Generating Actions: A Small Design Discussion

Here's a mildly interesting small-scale design choice. I'm generating a set of actions corresponding to a set of states which are represented by monotonically-increasing enums. (And when I say generating, I mean it: this is actually code produced by a code generation tool I'm writing, not manually.) Each concrete instance implements the interface: class ITransmogrifierAction { public: virtual ~ITransmogrifierAction(); virtual TestEnum type() const = 0; virtual std::pair<Token,TestEnum> process(const TokenType& inVal) = 0; }; and the array being filled - std::array<std::unique_ptr<ITransmogrifierAction>, 5> m_actions; - will end up being a lookup for state machine transitions. The obvious STL way to do this is via generate_n. class Filler { public: std::unique_ptr<ITransmogrifierAction> operator()() { switch (m_n++) { case 0: return std::make_unique<AlphaTransmogrifierAction>(); case 1: retur...