Reputation: 101484
G++4.8.2, gtest.
We are developing a code library (used internally) of classes which derive from a common base class, BasicGizmo
.
Today each class derived from BasicGizmo
is required to have an enum
member named Size
, which has the expected size of the class. For example:
#pragma (pack 1)
class SpecialGizmo
:
BasicGizmo
{
public:
enum {Size = 4};
uint32_t mSomethingInteresting;
};
class SuperGizmo
:
BasicGizmo
{
public:
enum {Size=8};
uint64_t mKewlData;
};
Elsewhere we are using gtest to construct unit tests to ensure that the actual sizeof
these classes is what we are expecting them to be according to the Size
enumeration:
CHECK_EQUAL (sizeof (SpecialGizmo), SpecialGizmo::Size); CHECK_EQUAL (sizeof (SuperGizmo), SuperGizmo::Size);
This all works as expected, except that the requirement that each of these derived classes both has a Size
enum and that there's a unit test to check it is only enforced through code reviews. I would rather have the compiler catch omissions before the code is ever even committed. To do so, I would need to have a way to automatically generate the unit tests for every class that is derived from BasicGizmo
.
Is any such mechanism possible?
Upvotes: 0
Views: 718
Reputation: 48635
Your scheme could break on different platforms or different compiler settings.
If I was going to do something like this I would consider generating the test code for a given platform. You could add all the class names to a file (a std::stringstream
in my example) and generate a testing program that outputs a reference information file on the size of each class/struct.
I would also consider engineering a way to include the platform and compiler settings at the beginning of the output reference information file.
If you make an official change to a class then you need to regenerate the test program to update the reference information file.
#include <string>
#include <iostream>
// These could be listed in a file
// opened with std::ifstream
std::istringstream iss
{
"ClassA\n"
"ClassB\n"
"ClassC\n"
"ClassD\n"
"ClassE\n"
};
int main()
{
// output an #include containing all the
// headers you want to test
std::cout << "#include \"all-classes.h\"" << '\n';
// needed for generated code
std::cout << "#include <iostream>\"" << '\n';
// output testing code
std::cout << "\nint main()\n{" << '\n';
std::string class_name;
while(std::getline(iss, class_name))
{
std::cout << "\tstd::cout << \"" + class_name + ": \" << sizeof(\"" + class_name + "\") << '\\n';" << '\n';
}
std::cout << "}" << '\n';
}
Output:
#include "all-classes.h"
#include <iostream>"
int main()
{
std::cout << "ClassA: " << sizeof("ClassA") << '\n';
std::cout << "ClassB: " << sizeof("ClassB") << '\n';
std::cout << "ClassC: " << sizeof("ClassC") << '\n';
std::cout << "ClassD: " << sizeof("ClassD") << '\n';
std::cout << "ClassE: " << sizeof("ClassE") << '\n';
}
Then compile the output and you have your test program.
Upvotes: 1
Reputation: 78
It would be better to use static_assert:
template<typename tp_DerivedFromGizmo> struct
t_SizeEnumMemberChecker
{
// you can also check that tp_DerivedFromGizmo really derives from BasicGizmo
static const bool result = (sizeof(tp_DerivedFromGizmo) == tp_DerivedFromGizmo::Size);
};
static_assert(t_SizeEnumMemberChecker<SpecialGizmo>::result, "");
Upvotes: 1