Posts

Moving Conversion from Caller to Callee

Consider this: you are maintaining a C++ codebase which uses strings. You have some lower-level methods which take/use C-style string, and you have some higher-level methods which use a string wrapper type (we'll use std::string for example purposes). Let's say you're going to convert the lower level code from direct pointers to something like std::string_view. In the above case, there is an automatic conversion, re both types being in the std library, so everything just works. But, if you have a large codebase, you have other string types (MFC, other), which may not automatically convert. You may end up writing some inline conversion to call the methods, or you may end up writing some wrapper methods to do the conversion at the call site. Both of these, though, are sub-optimal: the lower-level method is taking a logical string, and there shouldn't need to be additional intellectual or code burden at the call site to convert from one logical string type to another. Ente...

Member Variable Initialization via With[...] Methods

There are a number of ways to initialize member variables in C++. One of the "recommended" methods is via a constructor, and arguments to it. While this is functional, I've personally been gravitating away from this paradigm in my own code of late, especially when there are multiple parameters involved, or multiple constructor overloads for different initialization scenarios. As the use-cases get more complex, it becomes harder to reason about the parameters passed, and more prone to maintenance-related errors. The alternative method I'm gravitating toward is this: class CFoo {      int nSomeValue = 0;      inline auto& WithSomeValue( int nValue )      {           nSomeValue = nValue;           return *this;      } }; Why is this "better" (for some subjective measurement of better)? Members are named and self-explanatory, rather than positional You do not ha...

std::optional<> is Useful

Consider this: How many times have you written code where a specific value for a type represents an implicit "empty" value? For example, a string value where an empty string was implicitly "no value"? A number where zero was the "default", "no value" case? How many times has that bitten you later, where you needed to add secondary variables to indicate if the first value was valid or not? Alternatively, how many data structures have values which may or may not be populated by a caller, and the callee needs to rely on additional or implicit information to discern if the values are valid or not? Enter std::optional<> (or previously boost::optional<>). This type can be used to express the "null" state for any type, without using secondary variables, implicit value meanings, or other mechanisms (eg: using pointers which might be null). Usage is easy: std::optional<std::string> osValue; osValue.has_value(); // yields false ...

C++0x, using decltype to implement property getters/setters

I realize this is old-hat/obvious to many people, but I found it interesting... With the addition of decltype to C++, it's now possible to write totally generic getter/setter methods to expose member variables, which do not need to be updated if the type of the member is later updated. To wit: Obviously, just remove the non-const members to create a read-only accessor, etc. It doesn't really save on typing or anything, but it might be nice as a "standard format" accessor template and such.

Variable types and sizes

Just a random thought on variable sizes: There's an old design decision for programming languages as to whether the variable size for built-in numeric types be static or dynamic. For example, the .NET framework has static sizes: every type in the System namespace (which are all the built-in types) has a size associated with it, eg: Int32. Conversely, in C/C++, int is of dynamic size, dependent on the compiler and the target environment. There are arguments for both. On the static size, you have deterministic size, so you can predict exactly what values will/won't fit. On the dynamic side, you can automatically use the size which is appropriate for the architecture, which gives you automatic adaptability to architectures with new intrinsic data sizes (eg: 32bit -> 64bit) without speed degradation from extra operations to adapt non-standard variable sizes. With those trade-offs in mind, I'd propose a new? thought for variable declarations: a concept of "at least"...

The enormous problem with highly dynamic languages

So you're moving from an "old-school" language like C/C++ into a "new hotness" language like C#, and life is so much easier. Memory management is automatic, type information is part of the runtime, everything is dynamic. You can create these very simple expression to perform very complex operations, all auto-magically, and rapidly prototype applications like never before. Life is great, right? Well, there's a small problem which is the 800lb gorilla in the ideology. See, there's two parts to a language enabling you to writing code which does what you want: letting you express what you want to do, and helping you not express what you didn't want to do. The former is aided by higher level abstractions, patterns, powerful expression syntax, API libraries, etc. The latter is aided by strong compile-time checking, API/structure transparency, clear and predictable behavior, etc. A good language balances both of these. The problem with low-level languages i...

Fun with COM interop

So I have a C# object which exposes a COM interface through interop, and it was working. Then I did something, and when I went to reload it, it said the component was not registered. I confirmed that the ProgID was in the registry, and everything appeared to be good. ... It turns out that if the constructor for an object which is being constructed for a COM object instantiation throws an exception, the CoCreateInstance call will return that the component is not registered. This is not normally a problem with native C++ COM objects, since they run the constructor code as part of the registration process, so you'd catch the error earlier. However, C# COM object apparently do not, and the error coming from COM is very misleading. Just an interesting tidbit for COM interop.