Forcing Construction of Global Objects in Static Libraries

Suppose you have a global object whose constructor does useful stuff – say, registration somewhere or initialization of global resources. Suppose further this object isn’t directly accessed anywhere – you just need the functionality in its ctor (which might trigger other functionality eventually. All is fine, until we add the last assumption: suppose this object lies in a static library.

This seems to be a long lasting pain, ultimately arising from the old (‘broken’? let’s just say ‘outdated’) C++ compiler-linker model. The way the linker works is by repeatedly searching for implementations of yet-unresolved referenced symbols, and including only the obj files with such implementations – thereby dropping entirely obj files with no external references, such as the one containing the global object whose ctor you need to run.

To make things concrete, take the following toy example:


//main.cpp
#include <tchar.h>

int _tmain(int argc, _TCHAR* argv[])
{
	return 0;
}

//GlobalInLib.cpp – compile as static lib
#include <stdio.h>
#include <tchar.h>

struct UsefulCtor
{
	UsefulCtor()  { _tprintf(_T("ThereIsNoSpoon")); }
};

UsefulCtor MyGlobalObj;

Under normal linkage, MyGlobalObj would be ignored. You can verify this either by putting a breakpoint in its constructor and see that it is never hit, or inspecting the output console window and see that it is empty.

<Aside> An interesting discussion arose a while ago in MS forums on whether this behavior violates the standard. Here, einros writes:

The C++ standard, section 3.7.1, specifies:

“If an object of static storage duration has initialization or a destructor with side effects, it shall not be eliminated
even if it appears to be unused, [...]“

But MS’ Holder Grund clarifies –

[Your quote of the standard] only holds if the corresponding translation unit is part of the program. In my definition and the one of at least four major toolchain implementators, it is not.

</Aside>

Enter ‘Use Library Dependency Inputs’.

This arcane combo box in the project references dialog has the sole documented effect of enabling incremental linking for static libs, but the interesting part is how it does it:

When this property is set to Yes, the project system links in the .obj files for .libs produced by dependent projects, thus enabling incremental linking.

And indeed, setting this option to True causes construction of MyGlobalObj in the example above.

Turns out you can force construction of globals in static libs after all.

 

 

Addendum: Only after writing this post did I come across this excellent 2005->2012 thread, which mentions this setting as a solution. Still, this effect of the linker is all but undocumented, and qualifies as deserving-more-web-presence.

My Guest Post on the VC Team blog

I answered a public invitation by Eric Battalio of the VC team – and just now published an article on the VC blog, introducing the native Expression Evaluator:

Every time you use the Watch window, a lot is going on behind the scenes. Whenever you type a variable name, something needs to map that name to the memory address and type of the named variable, then display that variable, properly formatted based on its type. Conversely, when you modify the contents of a variable – something needs to take your text input, convert it to the right type and correctly update the memory at the right address.

That something is the Expression Evaluator. It is an impressive and often overlooked piece of technology and once familiar with it, you can put it to good use, sometimes in surprising ways!

Check it out!

Geometric Inverse Application 1: Barycentric Coordinates

Last time I jotted down some equations suggesting how you should understand 3d matrix inverses, or how to solve 3×3 equations. Below is a first application, for obtaining barycentric coordinates.

Barycentric coordinates are the canonical way of describing a point within a triangle (or more generally, within a polygon, or just any convex point set). Briefly put, suppose you’re given a triangle with vertices A, B & C, and an interim point P:

P’s position relative to A, B and C can be described by a set of 3 scalars, say α, β & γ, called its barycentric coordinates:

For P to lie in the plane formed by A, B & C these ingredients must satisfy –

And for P to lie within the triangle, they must satisfy –

You can think of these equations as describing a recipe for cooking up P from the ingredients A, B & C: α as the amount of A you need to put in, β the amount of B γ of C. These coordinates are very useful, for example, for interpolation: quantities that are stored for A, B & C can be mixed with the same coefficients and applied to P.

Now how do you actually find barycentric coordinates? Well, the equation defining them can be rewritten in matrix form:

Which gives a still-not-very-explicit expression for the coordinates:

The derivation in the previous post gives a way to deduce expressions for each coordinate. Say, for α:

And similarly:

For some extra geometric flavour, note that these quotients can be understood as ratios of areas: α is the ratio of the area of the triangle P-B-C to the area of the full triangle A-B-C:

Finally, a correction of an apparently common misconception. I’ve heard a few times the interpretation of barycentric coordinates as a expressing distances of some sort – it is indeed tempting to think that if α is close to 1 then P’s distance from A is small. That just isn’t true. For example in this setup –

A is the triangle vertex closest to P, and still the α coordinate is zero – as low as it can get. When ‘cooking up’ P, we have to mix in only B and C – with no A at all.

Geometric Interpretation of a 3D Matrix Inverse

I work a lot with 3D calculations, and every so often a non trivial 3D tidbit comes along. Some of these might be of use to others – and so, by the power vested in me as absolute monarch of this blog, I hereby extend its scope to some light 3D geometry. I’ll try to keep posts in this category less rigorous and yet more verbose than regular math write-ups.

Take a 3×3 matrix that you wish to invert, say M. Think of its columns as 3-dimensional vectors, A, B & C:

image

Now take it’s sought-after inverse, M-1, and think of its rows as 3D vectors, say v, u & w. That means essentially:

image

Next focus just on v, M-1 ‘s first row. What can be said of it, in terms of A, B & C?  Looking in the first row of the multiplication result – the identity matrix – we see:

image

Which means in particular that v is orthogonal to both B and C. Assuming B and C aren’t co-linear (otherwise M wouldn’t be invertible in the first place) there is but a single direction in 3D space which is perpendicular to both, and it can be written as B×C  – vector-product or cross-product of B and C.  Hence v must be a multiplication by some scalar – say α – of this direction:image

To deduce α remember the v must be normalized so that its dot product with A gives 1.  And so:image

The triple product in the denominator, A∙(B×C), should look familiar: that is in fact det(M) – the determinant of the original matrix. Had we inverted M with a more traditional apparatus, say Kramer’s rule, we would have divided by this determinant directly.

Naturally similar expressions are obtained for the other rows, u & w :

image

All the denominators are in fact equal, to each other and to det(M).

Why all the hassle?

First, for the fun of it. Personally I find it much easier to understand – and thus remember – geometric stories than algebraic ones.

Second, this formulation exposes several optimization opportunities.

  1. After computing B×C you can obtain the first of (and so all of) the denominators, by simply taking a dot product with A.
  2. If you need just a single row of the inverse matrix, you can calculate it directly – without having to invert the entire matrix.
    This is not as far fetched as it might seem: say you formulate a 3×3 linear equation set, but you’re actually interested only in the 1st solution coordinate:imageJust take the 1st row of M’s inverse, as outlined above, and dot-product it with b:
    image

Third, using analytical expressions as above for solving linear equations is generally preferable to numeric solvers. For matrices as small as 3×3, solving numerically would probably be a bad idea anyway – even traditional, tedious inverses (with adjoint matrices and all) would be preferable to numeric solutions.

BTW, higher dimensional analogues do exist – and are as easy to derive – but the main added value, namely direct geometric insight, is lost beyond three dimensions.

VS Support Policy

As far back as this MS support page goes, Visual studio editions had a 5-year mainstream support period, and since VS .NET 2003 – a 10 year extended support period. In particular, VS2010 mainstream support is advertised to end on Jul 14, 2015.

Now given that MS releases major new VS versions roughly once every 2 years, such a support period can be quite a burden. In my (much, much smaller) organization we don’t bother with backward support at all, we just ask those pesky cry-baby customers to upgrade to our latest version before we even consider checking their bug reports. The logistics of testing and patching multiple versions can admittedly get exhausting – so I would have had great respect to the magnitude of the task that DevDiv took upon themselves when they chose to support 2.5 versions backwards.

If they would have actually done so.

As of ~July 2012, the VS bug submission form in Connect no longer enables even reporting issues with VS versions prior to 2012 (note, I think that was before VS2012 even reached RTM):

image

Long before that, bugs I filed against VS2010, along with complete, consistent repros, were closed as either not reproducible or fixed – if they happened to be resolved in VS2012.

For all practical purposes, support in VS2010 ended less than 2 years after its release – and less than 1 year after its first service pack release!  In our organization (and I suspect in many others) we don’t even consider upgrading VS before the newest version has had its run, and reached a level of maturity attainable probably only at a service pack release. That leaves us with less than a year of practical support, which is beyond annoying – it borders on fraud.

I should probably post here more details about specific unresolved bugs I reported. Beyond that and the much needed venting, I don’t see much that can be done.