Posts

Showing posts from March, 2024

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...

State Machines

Almost any program can be modelled as a state machine. I say "almost any" because it is, just, possible to think of a program which cannot easily be resolved into anything more than a one-state state machine. Consider a command interpreter with no support for loops or function definitions (like the old DOS command.com) and with no internal commands, only the ability to execute external commands. It loops forever and does only one thing for every line it reads.    If it does no error handling, the execution is a single line, a call to execve or one of its relatives. There are internal stages inside the C library call, but they are effectively invisible to the program.  However, it's not a very useful program. Neither is any version of HelloWorld. The echo application does have its uses. Once one gets beyond this, even simple programs have at least two states: setup and execute. A simple version of cp has to open two files for input and output before beginning to copy data....