Monday, 8 May 2017

Qt and "a missing vtable usually means the first non-inline virtual..."


This post is a kind of a "thank you" for a really good piece of advice from a fellow programmer. As it is published on StackOverflow, and I for my part don't use it very much and cannot even upvote it, this post should spread the word about it instead!

Or, alternatively, you can see that post as another installment of an engineering notebook, next one in the series on bug hunting or another piece of advice for the googling programmer*.

The Problem:

I was checking in my changes including promoting a class to a QtObject and everything seemed to be working at first - I tested it locally, it seemed to build in TFS (Team Foundations Server, i.e. in our Windows CI build) but in TeamCity (i.e. in our Mac build) the build was broken with an ominous error message:
NOTE: a missing vtable usually means the first non-inline virtual member function has no definition.
And it came from the linker! Initially I supposed some Clang peculiarities (or even bugs) to be held responsible for that, and as I don't have a Mac to check this, I was a little lost for ideads.

The Solution:

But then I found this piece of advice:
Another possibility is that the class in question once didn't belong to Qt meta object system (that is, it had no Q_OBJECT or maybe didn't inherit from QObject at all), so qmake needs to be run again in order to create the necessary rules for MOC. The easiest way to force qmake to be run is to make some insignificant changes to the project file to update its timestamp, like adding and then removing some white space.
As I said, I cannot upvote it (you need at least 15 reputation points for that) so I just say thank you Sergey!

Because... that's exactly it! The missing virtual methods belonged to Qt's signal infrastructure which should be generated by the MOC compiler!

The error didn't manifest itself locally because I generated a Visual Studio project from Qt's .pro files, thus the new MOC file was forced to be created. Thus the problem disappeared trivially.

The same was done in our Windows CI build, but this time only Makefiles were generated and only the "naked" Microsoft compiler was invoked in command line mode to process them. Anyway, the same applies here, MOC creation is forced each time.

On the CI build server however, the "naked" .pro definition files are used, and qmake must be nudged a little to wake up and do its job.

The Moral:

Qmake doesn't like it when we change our minds ๐Ÿ˜‰.

--
* You've got it? Working mathematician etc..? ๐Ÿ˜Š

Friday, 24 February 2017

Advanced Stateful Lambdas


Earlier this week I listened to Episode 51 of C++ Weekly Youtube "show" ๐Ÿ˜‰ by @lefticus:
...and I just had to write this cool stuff down before I forget it!

So treat this post as a kind of engineering notebook - not my ideas, not my code, just a remainder and a reference. So without much ado, here's the code:
int main()
{
  auto fib = [a = 0, b = 0] () mutable {
    struct Results {
      int& a;
      int& b;

      Results next(int num = 1) {
        while (num > 0) {
          a = std::exchange(b, b + a);
          --num;
        }
        return *this;
      }
      
      operator int() {
        return a;
      }
    }

    return Results{a, b}.next();
  };

  // main
  return fib().next(5);

  // or:
  // return fib().next().next().next().next().next()

  // or:
  // return fib().next().a;
}
I won't comment on any part of the code, as not to take credit for work of others. If you want to know what this is about, just listen to this episode of C++ Weekly. Or even better, try to find it out by yourself and listen to it after that!

By the way, I support Jason on Patreon, and I think, you should support him too because of his fantastic job with C++ Weekly!

PS: here is another interesting code, this time  from episode 50, exploring generic lambdas and the "template if":
int intTotal = 0;
int doubleTotal = 0.0;
std::common_type<decltype(intTotal), decltype(doubleTotal)> grandTotal;

auto generic_visitor[&intTotal, &doubleTotal, &grandTotal] (const auto v) {
  grandTotal += v;

  if constexpr(std::is_same<double, decltype(v)>{} {
    doubleTotal += v;
  } else {
    intTotal += v;
  }
};