2020-04-30

Oh, my.

The Webbys.

Kinda says it all.

I had been getting cautiously optimistic that the leadership at Stack Exchange had understood (at least a little) just what they had come into control of and had adjusted their focus a little from pure numbers to something like actual utility. But this suggests a focus on mass popularity. So, for all the fine work that the remaining CMs and new public facing employees have been putting in, the ship remains on the same course.

2020-04-22

Little surprises #3: gdb is smart

Where should a debugger report errors that occur in object destructors?

The obvious answer is that they should report it on the line where it occurred, which is right as far as it goes. But in many cases the system standard library doesn't have debugging symbols so errors that occur in the object destructors of, for instance, standard containers won't report a line.

No worries, however, you can grab a stack trace to start finding out where in your code the stupid is being injected.1 But if you are using gdb you may be in for a surprise. Consider this little program
/* some preamble omitted for brevity */
class dtorcrash {
public:
  dtorcrash(int i): m_i(i) {}
  ~dtorcrash(){ std::cout << 10/m_i << std::endl; }// Line 8
private:
  int m_i;
};


int main(int argv, char**argc)
{
  int i = 0;
  if (argv > 1)
    i = atoi(argc[1]);
    
  dtorcrash * heap = nullptr;
  {
    dtorcrash local0(i++);                         // Line 22
    dtorcrash local1(i++);
    heap = new dtorcrash(i++);
    std::cout << "Constructed." << std::endl;
  }                                                // Line 26 
  delete heap;                                     // Line 27
  std::cout << "Destructed." << std::endl;

  return EXIT_SUCCESS;
}
If the command-line argument evaluates to zero, minus-one, or minus-two it will crash because the destructor of dtorcrash executes a division by zero.

The interesting thing is what we see when we examine the stack traces in gdb following those crashes.

If you pass -2 the back trace reads
#0  0x0000555555554d32 in dtorcrash::~dtorcrash (this=0x555555768e70, __in_chrg=) at dtorcrash.cpp:8
#1  0x0000555555554c29 in main (argv=2, argc=0x7fffffffdfc8) at dtorcrash.cpp:27
Here (as in all cases) the actual offending line is reported as the d'tor of dtorcrash. Further the location in the calling frame is identified as delete heap;. All as expected.

The interesting thing with the location in the calling frame if you supply 0 or 1 as the command line argument. In that case gdb does not identify the closing scoping brace on line 26 as the site of the offending call. Instead it identifies one of the declarations at the top of the scoping block. Even though the code will print Constructed. before crashing. For instance if we allow the argument to default to zero the back trace generated looks like
#0  0x0000555555554d32 in dtorcrash::~dtorcrash (this=0x7fffffffdeb4, __in_chrg=) at dtorcrash.cpp:8
#1  0x0000555555554c18 in main (argv=2, argc=0x7fffffffdfc8) at dtorcrash.cpp:22
where line 22 is the declaration of local0.

What the *&^% is going on?


The critical difference is that the weird behavior shows up with the crash is triggered by an object with automatic storage scope. For heap allocated objects there is an explicit call to delete.2

On the other hand the end of a scope can trigger the reaping of multiple objects (indeed, the sample code is written so that two objects are reaped at the same close-brace).

If gdb just told you that the crash occurred in a destructor triggered by that close-brace you wouldn't know which objected was responsible, so instead it points you at the declaration of the offending object.

This means that have to notice the destructor in the back-trace and then scan the code to find the lower limit of the scope associated with that declaration, but you know which object to be checking up on.

Nice.



1 Every novice programmer thinks they've found a bug in a core tool at some point. They're almost always wrong. In my case I spent a couple of hours sure there was something wrong with the system implementation of atan2. There wasn't. It's embarrassing.

More experienced programmers get so used to assuming that their own code is the problem that it can be very hard for them to convince themselves that the problem is in someone else's code.

2 It may be buried in some other objects d'tor, but it's there.

2020-04-18

Energy is frame-dependent, the infrastructure of energy is frame-independent

The concept of energy and the elaborate mathematical infrastructure that surrounds it are so fundamental to mechanics that they are generally introduced less than half-way through the first semester of the introductory course in a college classroom (true whether you are doing a force-first or momentum-first introduction).

But I'm pretty sure I'm not the only person to have said
"Wait a minute, how the #&%* did I miss that?!?"
when teaching that "elementary" infrastructure after using it for years. Not because I wasn't paying attention the first couple of times through (I had physics in high school before taking it in college), nor because "that" is deep or mysterious, but because I didn't have the context to appreciate the importance of "that" until much later in my education.

As far as I can tell there are several distinct things which qualify as "that" for various people, and today I want to talk about one of them.

Kinetic energy is frame dependent


Obvious if you think about it, right? I mean $T = \frac{1}{2} m v^2$ with $v$ depending on the frame.

But the Work-energy theorem
$$ W_\text{net} = \Delta T \;
$$ is fundamental to the whole infrastructure. How does that work (heh!) if kinetic energy depends on the observer's state of motion?

Work is frame dependent, too


Well, work depends on path length and *that* also depend on the observer's state of motion.
$$ \mathrm{d}W = \vec{F} \cdot \mathrm{d}\vec{s} \;,
$$ where the path $s$ can have a different shape and different length in different frames.

To illustrate how this works out let's consider a case in one dimension. Seen in frame $S$ a object of mass $m$ is subject to constant net force F starting from position $x_0$ with velocity $v_0$ so that it moves to position $x$ where it has velocity $v$. The choice to limit this to a constant acceleration case implies that
\begin{align}
x - x_0
&= \frac{v^2 - v_0^2}{2a} \nonumber\\
&= m\frac{v^2 - v_0^2}{2 F} \nonumber\\
F (x - x_0)& = \frac{1}{2} m (v^2 - v_0^2) \nonumber\\
W &= \Delta T
\end{align} as expected. or future reference, the elapsed time is $t = (v-v_0)/a = m(v-v_0)/F$.

Viewing the same event in frame $S'$ that moves with speed $-u$ with respect to frame $S$ and is initially co-located with $S$ we have
\begin{align}
\begin{aligned}
x_0' &= x_0 \\
x' &= x + u t \\
v_0' &= v_0 + u \\
v &= v + u \;,
\end{aligned}
\end{align} and of course $F' = F$; $m' = m$; $a' = a$; and $t' = t$.

We can perform the analysis in the primed frame two ways: we can use only primed symbols resulting in exactly the same work leading to
\begin{align}
W' = \Delta T' \;,
\end{align} or we can start with the primed quantities then switch to the un-primed quantities by substitution from (2). This give us
\begin{align}
x' - x_0' &= \frac{v'^2 - v_0'^2}{2 a} \nonumber\\
x - (x_0 + u t) &= \frac{1}{2a} \left[ (v + u)^2 - (v_o + u)^2 \right] \nonumber\\
\end{align} expanding the squares on the RHS and re-grouping leads to
\begin{align}
(x - x_0) + u t &= \frac{1}{2a} m\left[ v^2 - v_o^2 + 2u\left(v - v_0\right) \right] \;. \nonumber
\end{align}
Here we recognize the un-primed work and change in kinetic energy and also reduce the spare terms on the RHS to arrive at
\begin{align}
W + u t &= \Delta T + u t \;. \\
\end{align}

So changing frames changed the values of the work done and the difference in kinetic energy by the same amount, and the work-energy theorem continues to work even though the figures that appear in it can be very different.

Lessons


First of all, if you are using energy to work a problem you have to pick one frame of reference and stick with it because you can't meaningfully compare energies or works between frame of reference.

The second issue is a little more subtle. Most people who actually think about the classical energy infrastructure as taught in typical introductory books will eventually notice that the whole thing seems to rest on very arbitrary definitions. I mean, why should work be that formula, right? I intend to write a essay on why it isn't arbitrary at some point, but in the mean time it should be clear that not every conceivable foundation will have the property where the rules work as you change frames. So Galilean relativity puts strong limits on choice of infrastructure.

2020-04-16

Little surprises #2: Qt Designer tricks

Constant-correctness in C++ is not something that you can practically do half-way. You either do it everywhere and always or you don't do it unless some library you're interfacing with forces your hand. I'm in the former camp, and I think that this is a choice with right and wrong answers. Doing it is right.

As a result I've gotten into the habit of typing const after method signatures almost without thinking, so that sometimes the linter has to remind me that I'm changing a object's state in a method marked as constant. In doing so I recently stumbled on a oddity of Qt's Designer tool.

There is a difference between having something and knowing where to find it


The interesting thing about the Designer component of Qt Creator is that it generates code of a separate UI object (which you are expected to not edit) and a skeleton widget object for you to flesh out. That skeleton includes a pointer-member to manage the UI object (and of course, Designer generates the requisite c'tor and d'tor behavior to make that safe).

If I use Qt Creator to generate a minimal QWidget the header file it writes looks like this:
#ifndef FORM_H
#define FORM_H

#include 

namespace Ui {
class Form;
}

class Form : public QWidget
{
    Q_OBJECT

public:
    explicit Form(QWidget *parent = nullptr);
    ~Form();

private:
    Ui::Form *ui;
};

#endif // FORM_H

If I write a method for this class From::changeSubWidget(QWidget*) const, I won't be able to change the pointer this->ui, but I will be able to change the properties of any subwidgets contained in the UI::Form object at the other end of the pointer..

Of course this has nothing to do with Designer per se and everything to do with the having a pointer or reference member. More over there are places where this is definitely a feature rather than a bug.

Philosophy


In my personal universe a method of the widget class that changes the UI should not be marked const even if the particular implementation you've chosen (or let the designer chose) allows it. Because (a) it's not really sensible (the widget that the user sees on their screen is changing) and (b) it locks you into a group of implementation stratigies (you can't later decide to pull all those UI elements into your class proper, which is admittedly not a big issue in Qt).

2020-04-10

Powerful tools, non-locality, and learning curves

Powerful tools can make you a more efficient programmer. Powerful languages provide abstractions, facilities for encapsulation, and syntactic sugar that helps cut down on interconnections and expresses what you want to do rather than how it should be done. Meta build systems reduce the hassle of providing cross-platform support. Code comprehension tools catch simple errors, support auto-completion, and support re-factoring.

But all this power is not without its cost. The same object oriented language facilities that provide encapsulation combined with easy access to super-class facilities means that you can't tell from the code fragment obj.feature() where you should look for the definition of feature().1 The notion of inheritance has introduced a degree of non-locality into your code: not all the behavior of a software artifact is necessarily defined in scope where the object is defined.

"We can solve any problem by introducing an extra level of indirection."
David J. Wheeler

I learned to program in a series of pure imperative languages (Basic, Pascal, C, and '80s era Fortran) plus a little assembly (which is the very antithesis of abstract and expressive). When I first started using C++, the non-locality of definition gave me fits even in the C-with-classes style of code I was encountering and producing.2 I'd read enough that I knew what I was suppose to be getting in return for my confusion, but I wasn't experienced enough to always realize the gains.

…except for the problem of too many levels of indirection
Anonymous commentary

You can imagine how appalled I was when I started bumping into slightly more sophisticated applications of object-oriented design (i.e. the gang-of-four "command" pattern) without any preparatory grounding in the techniques. Now the meaning was scattered over even more parts of the code base!3

I had a strong impulse to avoid those kinds of tools and roll my own solutions using the techniques I was familiar with, and sometime gave in.

But I was reading a little around the internet and bits were sinking in. I started reinventing some of the basic principles (and reinventing all the ways to screw up, natch). And of course, having "thought of it" myself I could appreciate the reasons better. That's one way to climb the learning curve and it has its advantages if you have the time, but I suspect that a more organized (or even formal) approach is generally better.

In any case, the point is that powerful tools can seem like a step backward until you get to know them.

GUI designers


Now consider GUI designers in general (and Designer component of Qt Creator in particular). They are powerful tools that can write a large volume of repetitive boilerplate code for you. And one of the specific features that Qt Creator provides is the ability to plumb together the signals and slots of multiple components that appear in a single form.

Which is great, but it means that there is a new place to look for information about your program (in the form designer view or in the generated code). And for the signals/slots feature of Qt you have to look into a specific view as that stuff doesn't show in the design view.

So you end up with part o the description of your widget in the class you write and part in a generated file which you can read but isn't really intended for that. And to find the data about the generated code in a form intended for your perusal you have to look in a different tool than your text editor (and a complicated tool at that). Non-locality writ large.

All in all, it's not surprising that one of my team members is resisting using the designer; not withstanding that (as far as my searches about the web can tell) most experts suggest using designer tools when practical rather than hand-tooling your GUIs.4




1 Though of course it is another powerful tool (the code comprehension facilities found in IDEs) that eases the burden.

2 I didn't help that I was using bare emacs without even the very basic code comprehension tools that were available for the editor at the time. Not so much as ctags.


Closeup of the Hunt–Lenox_Globe showing the legendary legend. Due thanks to Wikipedia.
3 Not withstanding that the familiar C library functions bsearch and qsort work on similar principles. But at least the presence of function-pointer served as a clear warning in C. In an object oriented context this stuff can sneak up on you as it only requires the combination of polymorphism and either reference or pointer members (polymorphism is equivalent to function pointers of course, but it doesn't look like them).

4 Mixing some hand tooled code and some generated code in the same project is, in my opinion, generally worse that choosing either case and sticking to it. If only because now you can't even anticipate which search path you will have to follow and you have to know all about both ways of finding stuff.

2020-04-09

Didn't get the survey

Ah. I see that the promised surveys have been sent out to Stack Exchange moderators who stepped down during the recent unpleasantness.

I didn't get one. But then they haven't gotten around to removing my diamond, either. I suppose that's because I didn't make use of any official channels to tell them about my decision, but that's their problem.

I mean, my resignation post on the Physics meta was featured and got more than 80 votes, I changed my username to indicate my new status, and I posted a few comments on the mother meta and Stack Overflow meta, and one of those said explicitly that I'd left.

I know some staff members saw them.

I conclude that the organization is either trying to pretend that some of this stuff didn't really happen or just doesn't give a fig about most of the network.



Aside: I'm sure the "Arrogance" tag applies. But is it for me or for them?