Всем привет.
Продолжаю экспертизу gtest/gmock.
Количество ошибок и багов зашкаливает.
Ничего удивительного, учитывая то,
как плохо они тестирует собственный фреймворк для тестов.
У них всего два сервиса: на одном тестируется сборка gcc,
на другом - вижуал студия.
О том, что бы тестировать для всех заявленных платформ,
всеми заявленными компиляторами и речи не идет.
В результате, о возникающих проблемах они узнают
в лучшем случае от "сознательных" граждан,
что пишут гневные баг-репорты,
и шлют им пулл-реквесты.
И вот, похоже, что теперь такая порочная практика привела к новой проблеме:
Нарушения легаси.
Фреймворк перестал собираться на старых линейках вижуал студии.
Следующий код иллюстрирует проблему:
https://rextester.com/FKX1017
C++ |
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
| #include <iostream>
template<class t>
struct first
{
using type = t;
};
template<class t>
struct next : first<t>
{
using type = typename next::first::type;
type val = 333;
};
int main()
{
next<int> sample;
std::cout << "val = " << sample.val << '\n';
} |
|
output:
Код прекрасно собирается и работает.
Не только gcc, но и новыми компиляторами от Visual Studio
Например, компилятор от Visual Studio 2015 update 3
Однако, вот эта строка немножко напрягает:
C++ |
1
| using type = typename next::first::type; |
|
Что об этом говорит стандарт языка?
С каких это пор можно обращаться к элементам шаблонно-класса,
без указания параметров шаблона?
Всегда думала, что опускать параметры шаблона класса
можно только для методов этого же класса.
Похоже, что более старые компиляторы (Visual Studio 2015 update 1)
рассуждают так же, как и я:
https://rextester.com/ACJ53272
Код
source_file.cpp(13): error C2039: 'first<int>': is not a member of 'next<int>'
source_file.cpp(20): note: see reference to class template instantiation 'next<int>' being compiled
Microsoft (R) C/C++ Optimizing Compiler Version 19.00.23506 for x64
Привожу код к более каноничному виду, и сборка завершается успехом:
C++ |
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
| #include <iostream>
template<class t>
struct first
{
using type = t;
};
template<class t>
struct next : first<t>
{
using base = first<t>;
using type = typename base::type;
type val = 333;
};
int main()
{
next<int> sample;
std::cout << "val = " << sample.val << '\n';
} |
|
Учитывая, что gtest прекрасно собирался и работал
на всей линейке студий начиная с 2008 (а может и 2003?)
Можно сделать неутешительный вывод:
поддержка фреймворка никакая.
за качество никто не отвечает.
количество багов растет.
качество кода - падает.
использование - на свой страх и риск.
и этот страх более чем обоснован.
фрагменты реального проблемного кода
файл: gtest-internal.h
https://rextester.com/OXKC11289
C++ |
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
| #include <iostream>
// Backport of std::index_sequence.
template <size_t... Is>
struct IndexSequence {
using type = IndexSequence;
};
// Double the IndexSequence, and one if plus_one is true.
template <bool plus_one, typename T, size_t sizeofT>
struct DoubleSequence;
template <size_t... I, size_t sizeofT>
struct DoubleSequence<true, IndexSequence<I...>, sizeofT> {
using type = IndexSequence<I..., (sizeofT + I)..., 2 * sizeofT>;
};
template <size_t... I, size_t sizeofT>
struct DoubleSequence<false, IndexSequence<I...>, sizeofT> {
using type = IndexSequence<I..., (sizeofT + I)...>;
};
// Backport of std::make_index_sequence.
// It uses O(ln(N)) instantiation depth.
template <size_t N>
struct MakeIndexSequence
: DoubleSequence<N % 2 == 1, typename MakeIndexSequence<N / 2>::type,
N / 2>::type {};
template <>
struct MakeIndexSequence<0> : IndexSequence<> {};
// FIXME: This implementation of ElemFromList is O(1) in instantiation depth,
// but it is O(N^2) in total instantiations. Not sure if this is the best
// tradeoff, as it will make it somewhat slow to compile.
template <typename T, size_t, size_t>
struct ElemFromListImpl {};
template <typename T, size_t I>
struct ElemFromListImpl<T, I, I> {
using type = T;
};
// Get the Nth element from T...
// It uses O(1) instantiation depth.
template <size_t N, typename I, typename... T>
struct ElemFromList;
template <size_t N, size_t... I, typename... T>
struct ElemFromList<N, IndexSequence<I...>, T...>
: ElemFromListImpl<T, N, I>... {};
template <typename... T>
class FlatTuple;
template <typename Derived, size_t I>
struct FlatTupleElemBase;
template <typename... T, size_t I>
struct FlatTupleElemBase<FlatTuple<T...>, I> {
using value_type =
typename ElemFromList<I, typename MakeIndexSequence<sizeof...(T)>::type,
T...>::type;
FlatTupleElemBase() = default;
explicit FlatTupleElemBase(value_type t) : value(std::move(t)) {}
value_type value;
};
template <typename Derived, typename Idx>
struct FlatTupleBase;
template <size_t... Idx, typename... T>
struct FlatTupleBase<FlatTuple<T...>, IndexSequence<Idx...>>
: FlatTupleElemBase<FlatTuple<T...>, Idx>... {
using Indices = IndexSequence<Idx...>;
FlatTupleBase() = default;
explicit FlatTupleBase(T... t)
: FlatTupleElemBase<FlatTuple<T...>, Idx>(std::move(t))... {}
};
// Analog to std::tuple but with different tradeoffs.
// This class minimizes the template instantiation depth, thus allowing more
// elements that std::tuple would. std::tuple has been seen to require an
// instantiation depth of more than 10x the number of elements in some
// implementations.
// FlatTuple and ElemFromList are not recursive and have a fixed depth
// regardless of T...
// MakeIndexSequence, on the other hand, it is recursive but with an
// instantiation depth of O(ln(N)).
template <typename... T>
class FlatTuple: private FlatTupleBase<FlatTuple<T...>, typename MakeIndexSequence<sizeof...(T)>::type>
{
using Indices = typename FlatTuple::FlatTupleBase::Indices;
public:
FlatTuple() = default;
explicit FlatTuple(T... t) : FlatTuple::FlatTupleBase(std::move(t)...) {}
template <size_t I>
const typename ElemFromList<I, Indices, T...>::type& Get() const {
return static_cast<const FlatTupleElemBase<FlatTuple, I>*>(this)->value;
}
template <size_t I>
typename ElemFromList<I, Indices, T...>::type& Get() {
return static_cast<FlatTupleElemBase<FlatTuple, I>*>(this)->value;
}
};
int main()
{
using type = FlatTuple<bool, bool>;
type val(true, true);
(void)val;
} |
|
В качестве ремонта, я заменяю фрагмент:
C++ |
1
| using Indices = typename FlatTuple::FlatTupleBase::Indices; |
|
На:
C++ |
1
2
| using Base = FlatTupleBase<FlatTuple<T...>, typename MakeIndexSequence<sizeof...(T)>::type>;
using Indices = typename Base::Indices; |
|
И сборка проходит успешно:
C++ |
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
| #include <iostream>
// Backport of std::index_sequence.
template <size_t... Is>
struct IndexSequence {
using type = IndexSequence;
};
// Double the IndexSequence, and one if plus_one is true.
template <bool plus_one, typename T, size_t sizeofT>
struct DoubleSequence;
template <size_t... I, size_t sizeofT>
struct DoubleSequence<true, IndexSequence<I...>, sizeofT> {
using type = IndexSequence<I..., (sizeofT + I)..., 2 * sizeofT>;
};
template <size_t... I, size_t sizeofT>
struct DoubleSequence<false, IndexSequence<I...>, sizeofT> {
using type = IndexSequence<I..., (sizeofT + I)...>;
};
// Backport of std::make_index_sequence.
// It uses O(ln(N)) instantiation depth.
template <size_t N>
struct MakeIndexSequence
: DoubleSequence<N % 2 == 1, typename MakeIndexSequence<N / 2>::type,
N / 2>::type {};
template <>
struct MakeIndexSequence<0> : IndexSequence<> {};
// FIXME: This implementation of ElemFromList is O(1) in instantiation depth,
// but it is O(N^2) in total instantiations. Not sure if this is the best
// tradeoff, as it will make it somewhat slow to compile.
template <typename T, size_t, size_t>
struct ElemFromListImpl {};
template <typename T, size_t I>
struct ElemFromListImpl<T, I, I> {
using type = T;
};
// Get the Nth element from T...
// It uses O(1) instantiation depth.
template <size_t N, typename I, typename... T>
struct ElemFromList;
template <size_t N, size_t... I, typename... T>
struct ElemFromList<N, IndexSequence<I...>, T...>
: ElemFromListImpl<T, N, I>... {};
template <typename... T>
class FlatTuple;
template <typename Derived, size_t I>
struct FlatTupleElemBase;
template <typename... T, size_t I>
struct FlatTupleElemBase<FlatTuple<T...>, I> {
using value_type =
typename ElemFromList<I, typename MakeIndexSequence<sizeof...(T)>::type,
T...>::type;
FlatTupleElemBase() = default;
explicit FlatTupleElemBase(value_type t) : value(std::move(t)) {}
value_type value;
};
template <typename Derived, typename Idx>
struct FlatTupleBase;
template <size_t... Idx, typename... T>
struct FlatTupleBase<FlatTuple<T...>, IndexSequence<Idx...>>
: FlatTupleElemBase<FlatTuple<T...>, Idx>... {
using Indices = IndexSequence<Idx...>;
FlatTupleBase() = default;
explicit FlatTupleBase(T... t)
: FlatTupleElemBase<FlatTuple<T...>, Idx>(std::move(t))... {}
};
// Analog to std::tuple but with different tradeoffs.
// This class minimizes the template instantiation depth, thus allowing more
// elements that std::tuple would. std::tuple has been seen to require an
// instantiation depth of more than 10x the number of elements in some
// implementations.
// FlatTuple and ElemFromList are not recursive and have a fixed depth
// regardless of T...
// MakeIndexSequence, on the other hand, it is recursive but with an
// instantiation depth of O(ln(N)).
template <typename... T>
class FlatTuple: private FlatTupleBase<FlatTuple<T...>, typename MakeIndexSequence<sizeof...(T)>::type>
{
using Base = FlatTupleBase<FlatTuple<T...>, typename MakeIndexSequence<sizeof...(T)>::type>;
using Indices = typename Base::Indices;
public:
FlatTuple() = default;
explicit FlatTuple(T... t) : FlatTuple::FlatTupleBase(std::move(t)...) {}
template <size_t I>
const typename ElemFromList<I, Indices, T...>::type& Get() const {
return static_cast<const FlatTupleElemBase<FlatTuple, I>*>(this)->value;
}
template <size_t I>
typename ElemFromList<I, Indices, T...>::type& Get() {
return static_cast<FlatTupleElemBase<FlatTuple, I>*>(this)->value;
}
};
int main()
{
using type = FlatTuple<bool, bool>;
type val(true, true);
(void)val;
} |
|
По хорошему, нужно проверить всю линейку: 2008/2010/2012/2013/2015/2017/2019
И на различных компиляторах gcc/clang/etc
К сожалению, у меня нет таких ресурсов
Будет классно, если форумчане скачают фреймворк с офф-сайта,
выполнят сборку и запустят тесты, что идут в комплекте с фреймворком.
И отпишут в тему о результатах.
Так мы можем помочь этому замечательному фреймворку.
В настоящий момент у gtest проблема с компиляцией.
проблема с компиляций собственных тестов.
проблема с прохождением этих тестов.