The One-Definition Rule

We have been hit by the same bug twice already this year. It ends in a crash, and it took developers days to find it (in each case), even though it is reproducible on each run of unit tests. The code, after significant simplifications, looks like this:

namespace tests
{
  struct Fixture
  {
    std::vector<int> v1;
    std::vector<int> v2;

    static Fixture create();
  };

  TEST_CASE(test1)
  {
    auto fixture = Fixture::create();
    std::cout << "running test1";
  }
}

We are using a unit-testing framework that uses certain clever techniques, so that by only declaring the test, we are also registering it for being run. With this, anything related to testing one piece of functionality is neatly confined to a single file.

The problem is really not with macros. They basically expand to quite a simple construct, where the test is executed during the initialization of a global object. After further simplifications, the code looks like this.

namespace tests
{
  struct Fixture
  {
    std::vector<int> v1;
    std::vector<int> v2;

    static Fixture create();
  };

  // expanded from macro TEST_CASE(test1)
  struct UTF_test1_runner
  {
    UTF_test1_runner();               // test run here
  };

  UTF_test1_runner UTF_run_test1 {};  // init of a global

  UTF_test1_runner::UTF_test1_runner()
  {
    auto fixture = Fixture::create(); // test contents
    std::cout << "running test1" << std::endl;
  }
}

After the test contents have been run, the program crashes when the destructor of fixture is in progress. Can you see why?

Continue reading

Posted in programming | Tagged , , | 13 Comments

Concepts Lite vs enable_if

This post contains quite advanced material. I assume you are already familiar with Concepts Lite. For an overview of what Concepts Lite is, I recommend this proposal. Also, I have found this blog very useful regarding the details of and issues with concepts’ mechanics. One could look at Concepts Lite as three features:

  1. A superior alternative to enable_if (or overload hiding).
  2. The subsumption relation that enables the additional control of partial ordering in the overload resolution process.
  3. A convenient tool for building compile-time predicates that check for valid types and expressions.

In this post I will only focus on the first feature, and try to answer the question, “what do we need Concepts Lite for, given that we already have std::enable_if (and SFINAE)?” Continue reading

Posted in programming | Tagged , , , | 8 Comments

Is constructible?

Today I want to share with you something that really surprised me. Currently, Tomasz Kamiński and Ville Voutilainen are working on fixing a certain issue with std::optional’s converting constructors (which deserves a separate post). At some point, in the solution, they perform the following type-trait test:

is_constructible<T, U>::value || is_convertible<U, T>::value

If the varying order of T and U upsets you, rest assured that this is correct. This is how these traits are defined: is_constructible takes the created type first, whereas is_convertible takes the converted-to type second. But what really struck me here is the apparent redundancy. When type U is convertible to T does it not imply that T is constructible from U? Or in other words, if the following copy-initialization works:

T v = u;

The following direct-initialization:

T v (u);

should also work? Well, this is C++. It turns out that such expectation is not necessarily correct. Continue reading

Posted in programming | Tagged , , | 9 Comments

The cost of std::initializer_list

C++ aims to be a language for people working close to metal. It offers abstractions that cost no (or minimum) run-time or memory overhead. If you require zero-overhead abstractions, std::initializer_list may not be a tool for you. Continue reading

Posted in programming | Tagged , | 17 Comments

Competing constructors

We start with a known C++ gotcha:

std::vector<int> v (size_t(4), 2); // parentheses
std::vector<int> u {size_t(4), 2}; // braces
 
assert (v.size() == 4);
assert (u.size() == 2);
 
assert (v[0] == 2); // elements: {2, 2, 2, 2}
assert (u[0] == 4); // elements: {4, 2}

In this post I want to analyze the source of the problem a bit further, and offer some suggestions on class design. Continue reading

Posted in programming | Tagged , , , , | 7 Comments

Diagnosable validity

Certain combinations of types and expressions can make a C++ program ill-formed. “Ill-formed” is a term taken from the C++ Standard and it means that a program is not valid, and compiler must (in most of the cases) reject it. This is quite obvious:

int main()
{
  auto i = "some text".size(); // invalid expression
};

String literals do not have member functions, therefore compiler cannot accept this program, and must report an error. This puts a responsibility on programmers to learn which expressions and types are valid in a given context and use only these. Again, I am saying a very obvious thing.

What is less obvious is that there is a way in C++ to enter a type or expression of which we do not know if it is valid or not, in an isolated environment, where it does not render the entire program ill-formed, but instead it returns a yes-no (or rather valid-invalid) answer, which we can use at compile-time to make a decision how we want the program to behave. When requested, compiler can analyze all the declarations it has seen so far, and make an approximated judgement whether a given type or expression would make the program ill-formed or not, if used outside the isolated environment. The compiler’s approximated answer is not always correct, but it is just enough most of the time. Continue reading

Posted in programming | Tagged , , , , | 8 Comments

Sessions and object lifetimes

In this post we will see how C++ object lifetime can be used to control the duration of sessions: time spent owing and using a resource. The goal is to get a better understanding of what tools the language offers for using and sharing resources efficiently. Continue reading

Posted in programming | Tagged , , , | 6 Comments