0

I was trying to create a unit test framework for myself, and it worked fine until I made it into a static lib. The static container unit_base::_units is used before its construction.

I reproduced the issue with the following simpler version:

lib.lib

lib.h

#pragma once

#include <vector>
namespace est {
    class unit_base
    {
    public:
        unit_base();
        class unit_vec : public std::vector<unit_base*> {
        public:
            unit_vec();
            ~unit_vec();
        };
        static unit_vec& get_units();
        virtual bool run() const = 0;
    private:
        static unit_vec _units;
    };

    bool test_all();
}

lib.c

#include "lib.h"
#include <iostream>

namespace est {
    unit_base::unit_vec unit_base::_units;
    unit_base::unit_base() {
        _units.push_back(this);
        std::cout << "push unit to: " << &_units << std::endl;
    } 
    unit_base::unit_vec::unit_vec() {
        std::cout << "vec(): " << this << std::endl;
    }
    unit_base::unit_vec::~unit_vec() {
        std::cout << "~vec(): " << this << std::endl;
    }
    unit_base::unit_vec& unit_base::get_units()
    {
        return _units;
    }
    bool test_all()
    {
        std::cout << "before get_units" << std::endl;
        auto& units = unit_base::get_units();
        std::cout << "after get units" << std::endl;
        for(auto& u: units) {
            u->run();
        }
        return false;
    }
}

exe

test.cpp

#include "lib.h"
#include <iostream>

namespace est
{
    bool est_test_func();
    class est_test : public unit_base
    {
    public:
        est_test():
            unit_base() {}
        bool run() const override
        {
            return est_test_func();
        }
    };
    static est_test est_test_inst;
    bool est_test_func() {
        std::cout << "test" << std::endl;
        return true;
    }
}

int main(int argc, char** argv)
{
    est::test_all();
}

The exe prints:

push unit to: 0x7f72dc873170
vec(): 0x7f72dc873170
before get_units
after get units
~vec(): 0x7f72dc873170

I tested it on Windows and WSL, both resulted in an empty _units for test_all(). I must be doing something terribly wrong, so I am here for help.

I created a repo with CMakeLists: https://github.com/Lucipetus/problemic.git

Eugene
  • 111
  • 4
  • You have two different translation units that instantiate objects in static scope, that have a specific dependency between them. Unfortunately, C++ does not guarantee static initialization order in different translation units, see linked question for more information. – Sam Varshavchik Apr 18 '21 at 02:10
  • Thanks! I was naively expecting static variables would be initialized right before accessed, at least when my program was statically linked together (it did work when I built the sources as one program). I have to check out the standards @SamVarshavchik – Eugene Apr 18 '21 at 02:21

0 Answers0