LINUX.ORG.RU

Будь проклят тот день #3 [С++ template hell]

 ,


0

2

Снова MSVC против g++ :

template <typename TIndex>
struct CVertexManagerFixed
{
    template <typename TPathBuilder, typename TCompoundVertex>
    class CDataStorage : public TPathBuilder::template CDataStorage<TCompoundVertex>
    {
    public:
        using CDataStorageBase = typename TPathBuilder::template CDataStorage<TCompoundVertex>;
        using Vertex = TCompoundVertex;
        using Index = TIndex;

    public:
        inline CDataStorage(const u32 vertex_count);
        inline virtual ~CDataStorage();
        inline Vertex& get_node(const Index& vertex_id) const;
    };
};


#define TEMPLATE_SPECIALIZATION                           \
    template <typename TIndex> \
    template <typename TPathBuilder, typename TCompoundVertex>

#define CFixedVertexManager \
    CVertexManagerFixed<TIndex>::CDataStorage<TPathBuilder, TCompoundVertex>

TEMPLATE_SPECIALIZATION
inline CFixedVertexManager::CDataStorage(const u32 vertex_count)
    : CDataStorageBase(vertex_count)
{
}

TEMPLATE_SPECIALIZATION
inline typename CFixedVertexManager::Vertex& CFixedVertexManager::get_node(const Index& vertex_id) const
{
    VERIFY(vertex_id < m_max_node_count);
    VERIFY(is_visited(vertex_id));
    return *m_indexes[vertex_id].m_vertex;
}

Лог ошибок:

../src/test_cpp.cpp:54:34: error: non-template ‘CDataStorage’ used as template
     CVertexManagerFixed<TIndex>::CDataStorage<TPathBuilder, TCompoundVertex>
                                  ^
../src/test_cpp.cpp:63:17: note: in expansion of macro ‘CFixedVertexManager’
 inline typename CFixedVertexManager::Vertex& CFixedVertexManager::get_node(const Index& vertex_id) const
                 ^
../src/test_cpp.cpp:54:34: note: use ‘CVertexManagerFixed<TIndex>::template CDataStorage’ to indicate that it is a template
     CVertexManagerFixed<TIndex>::CDataStorage<TPathBuilder, TCompoundVertex>
                                  ^
../src/test_cpp.cpp:63:17: note: in expansion of macro ‘CFixedVertexManager’
 inline typename CFixedVertexManager::Vertex& CFixedVertexManager::get_node(const Index& vertex_id) const
                 ^
../src/test_cpp.cpp:54:59: error: expected unqualified-id before ‘,’ token
     CVertexManagerFixed<TIndex>::CDataStorage<TPathBuilder, TCompoundVertex>
                                                           ^
../src/test_cpp.cpp:63:46: note: in expansion of macro ‘CFixedVertexManager’
 inline typename CFixedVertexManager::Vertex& CFixedVertexManager::get_node(const Index& vertex_id) const
Я не понимаю, почему объявление возвращаемого типа метода get_node дает такую ошибку. Ведь CDataStorage - это только шаблон, нигде как класс он не объявлен?

★★★★★
Ответ на: комментарий от missxu

утечка памяи только в clang

Это не обязательно означает ошибку в компиляторе. Особенно в clang, выпоняющем довольно агрессивные оптимизации

P. S. Нихрена себе бомбит. Это мешает думать, успокойся

Deleted
()
Ответ на: комментарий от missxu

void* b2BlockAllocator::Allocate(int32 size) {
...
	if (size > b2_maxBlockSize)
	{
		return b2Alloc(size);
        }
...
    return m_freeLists[some_index];
...

Вы там из C# что ли пришли? Кто владеет объектом, определитесь уж. Стандартная ситуация с выращенными в тепличных условиях GC кодерами.

Deleted
()
Ответ на: комментарий от Deleted

Там же у b2BlockAllocator::Free два параметра — указать и size. По size затем в Free определяют, кто именно должен освобождать память.

Странный подход, конечно, но выглядит вполне себе рабочим.

eao197 ★★★★★
()
Ответ на: комментарий от eao197

По size затем в Free определяют, кто именно должен освобождать память.

Ну да. Но это же рукопашная. Кто-то передал (гипотетически) во Free не тот size - вот утечка. В этом разобраться - надо реально засесть надолго, вникнуть. Но «пахнущий» код дает направление, куда копать.

Deleted
()
Ответ на: комментарий от Deleted

Может быть еще проще. Ручной вызов Free в каком-то месте стоит рядышком с UB в коде. clang этот UB оптимизирует так, что Free выбрасывается, тогда как gcc или msvc могут не выбрасывать.

eao197 ★★★★★
()
Ответ на: комментарий от eao197

Да рабочая гипотеза. Как уже было сказано тут, отсутствие статического анализа и кросслатформенных сборок с -Werror не приблизит разрешения проблемы. Так же, как и нежелание участников проекта это понимать.

Deleted
()
Последнее исправление: Deleted (всего исправлений: 1)
Ответ на: комментарий от Deleted

С чего бы это? Это потому, что ваша студия из своих заголовочников сыплет ворнингами? Ну так, наводит на подозрения

(Не стоит упоминять, что множество ворнингов и строгость реакции на них - тюнится. Но оставлять это на «и так сойдет» не прокатывает, что и подтверждает тред.)

Deleted
()
Последнее исправление: Deleted (всего исправлений: 1)
Ответ на: комментарий от Deleted

множество ворнингов и строгость реакции на них - тюнится

Лол. Ну затюнь варнинг на сравнение знакового и беззнакового так, чтобы вот таких false-positive не было:

std::size_t max(...);
auto begin(...), end(...);
...
while (std::distance(begin,end) <= max) { ... }
И в тоже время был оскал на что-то вида:
unsigned i(0);
...
if (i == -1) { ... }

Deleted
()
Ответ на: комментарий от Deleted

Предупреждения должны быть для обоих этих случаев.

-Werror вызывает боль в двух случаях:

- когда используется внешняя либа, над исходниками который ты не властен (например, при компиляции Asio clang-ом выдается очень много предупреждений);

- когда переходишь на новую версию компилятора, в которой появились новые диагностики и новая версия компилятора стала ругаться на то, что старые версии пропускали.

Для собственного же кода -Werror больше полезен, чем вреден.

eao197 ★★★★★
()
Ответ на: комментарий от eao197

Предупреждения должны быть для обоих этих случаев

Нет. std::distance() всегда выдаёт неотрицательный результат, т. к. в другом случае всё равно UB:

The behavior is undefined if end is not reachable from begin by (possibly repeatedly) incrementing begin.

при компиляции Asio clang-ом выдается очень много предупреждений

Кстати, да(-Wzero-as-null-pointer под gcc - не то чтобы много, но образованию простыни способствует)

Deleted
()
Ответ на: комментарий от Deleted

The value may be negative if random-access iterators are used and first is reachable from last (since C++11)

Deleted
()
Ответ на: комментарий от Deleted

Нет. std::distance() всегда выдаёт неотрицательный результат, т. к. в другом случае всё равно UB:

The behavior is undefined if end is not reachable from begin by (possibly repeatedly) incrementing begin.

Вроде как в C++11 и последующих условие другое:

If InputIt is not RandomAccessIterator, the behavior is undefined if last is not reachable from first by (possibly repeatedly) incrementing first. If InputIt is RandomAccessIterator, the behavior is undefined if last is not reachable from first and first is not reachable from last.

https://en.cppreference.com/w/cpp/iterator/distance

eao197 ★★★★★
()
Ответ на: комментарий от Deleted

std::distance():
The behavior is undefined if end is not reachable from begin by (possibly repeatedly) incrementing begin.

В других случаях UB наступает ещё раньше, там уж не до сравнения

Deleted
()
Ответ на: комментарий от eao197

Да, но warning есть не только для RandomAccessIterator

Deleted
()
Ответ на: комментарий от Deleted
#include <list>
#include <iterator>

#include <cstddef>

int main()
{
	std::size_t s(3U);
	std::list<int> l{0,1,2};
	return std::distance(l.cbegin(),l.cend()) > s;
}
$ g++ -std=c++14 -pedantic-errors -Wall -Wextra -xc++ -Wsign-compare -c -o/dev/null main.c++ 
main.c++: In function 'int main()':
main.c++:10:44: warning: comparison between signed and unsigned integer expressions [-Wsign-compare]
  return std::distance(l.cbegin(),l.cend()) > s;
         ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^~~
$ g++ --version
g++ (Debian 6.3.0-18+deb9u1) 6.3.0 20170516
Copyright (C) 2016 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.  There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

Есть, куда без него )

Deleted
()
Ответ на: комментарий от Deleted

Не понимаю, о чем тут спор. Кто лучше оправдает подозрительный код?

warning: comparison between signed and unsigned integer expressions

такие ворнинги бесят, да. Но по факту они правильные. Есть сомнения в адекватности стандарта? Даже если и так, то противоречить ему в коде - себе дороже.

Ну ptrdiff_t - это, даже на пальцах, совсем не то что size_t. Бывает, выйти из кругов ада с этим связанных очень не просто - другой вопрос...

Deleted
()
Ответ на: комментарий от Deleted

Но по факту они правильные

Нет же. Ты сам выше цитату приводил, отрицательное значение может возвращаеться при передаче RandomAccessIterator. Для остальных категорий итераторов возврат всегда положительный(там я уже цитировал). Я привёл пример, где нет RandomAccessIterator-а(соответственно, возврат положительный и сравнение проканает) - а warning всё равно есть(априори бесполезный).

такие ворнинги бесят, да

А с -Werror одними матюками не ограничишься - надо будет что-то менять

Ну ptrdiff_t - это, даже на пальцах, совсем не то что size_t. Бывает, выйти из кругов ада с этим связанных очень не просто - другой вопрос...

С этим не спорю(как и с нужностью warning-ов), я против именно -Werror во все поля

Deleted
()
Ответ на: комментарий от Deleted

Я привёл пример, где нет RandomAccessIterator-а(соответственно, возврат положительный и сравнение проканает) - а warning всё равно есть(априори бесполезный).

Warning есть потому, что прототип std::distance не меняется в зависимости от типа итератора. Как возвращался difference_type, так и возвращается.

И это проблема стандарта, а не компилятора.

Польза от warning-е есть: сегодня у вас в коде std::list, завтра — std::vector. А если вы пишете обобщенный код, то вообще можете не знать, какой тип контейнера вам подсунут.

eao197 ★★★★★
()

Нашел решение. убрал using Vertex = TCompoundVertex;
и ниже заменил все включения этого типа параметром TCompoundVertex

#include <iostream>
#include <cstdint>

using s8 = std::int8_t;
using u8 = std::uint8_t;

using s16 = std::int16_t;
using u16 = std::uint16_t;

using s32 = std::int32_t;
using u32 = std::uint32_t;

using s64 = std::int64_t;
using u64 = std::uint64_t;

using f32 = float;
using f64 = double;

using pstr = char*;
using pcstr = const char*;

template <typename TIndex>
struct CVertexManagerFixed
{
    template <typename TPathBuilder, typename TCompoundVertex>
    class CDataStorage : public TPathBuilder::template CDataStorage<TCompoundVertex>
    {
    public:
        using CDataStorageBase = typename TPathBuilder::template CDataStorage<TCompoundVertex>;

    public:
        inline CDataStorage(const u32 vertex_count);
        inline virtual ~CDataStorage();
        inline TCompoundVertex& get_node(const TIndex& vertex_id) const;
    };
};


#define TEMPLATE_SPECIALIZATION                           \
    template <typename TIndex> \
    template <typename TPathBuilder, typename TCompoundVertex>

#define CFixedVertexManager \
    CVertexManagerFixed<TIndex>::CDataStorage<TPathBuilder, TCompoundVertex>

TEMPLATE_SPECIALIZATION
inline CFixedVertexManager::CDataStorage(const u32 vertex_count)
    : CDataStorageBase(vertex_count)
{
}

TEMPLATE_SPECIALIZATION
inline TCompoundVertex& CFixedVertexManager::get_node(const TIndex& vertex_id) const
{
    VERIFY(is_visited(vertex_id));
}

eagleivg ★★★★★
() автор топика
Ответ на: комментарий от eao197

вообще можете не знать, какой тип контейнера вам подсунут.

... тогда оно при инстансе и выстрелило бы!

И это проблема стандарта

ну типа того. Но не очень значительная.

ЗЫ Ох, вот о чем не говорят почему-то - так это скорость компиляции. Боже, мне с 3 часовой дебажной сборкой так дико смотреть на _СЕКУНДЫ_ сборки огромных C# проектов. Я не верю, просто не верю.

Deleted
()
Ответ на: комментарий от eao197

прототип std::distance не меняется в зависимости от типа итератора

template< class InputIt >
typename std::iterator_traits<InputIt>::difference_type
distance( InputIt first, InputIt last );

Таки меняется, или я не правильно понял, что имелось ввиду

А если вы пишете обобщенный код, то вообще можете не знать, какой тип контейнера вам подсунут

Но я всегда знаю(в своих кейсах, разумеется), что end достижим из begin

Deleted
()
Ответ на: комментарий от eagleivg

убрал using и сделал всё вручную

Пипец какой-то. using это прям больное место компиляторов

Deleted
()
Ответ на: комментарий от Deleted

Ох, вот о чем не говорят почему-то - так это скорость компиляции.

А с этим кардинально вряд ли что-то сделаешь. Даже модули, будут они сделаны, могут принципиально ничего не поменять, т.к. уже сейчас на сложном шаблонном коде львиная доля времени тратится не на чтение и парсинг include-ов, а на инстанциирование шаблонов и оптимизацию получившегося добра.

Тут разве что переход на другой ЯП поможет :(

eao197 ★★★★★
()
Ответ на: комментарий от Deleted

Таки меняется, или я не правильно понял, что имелось ввиду

Имелось в виду, что в конкретных случаях с std::list и std::vector за difference_type скрывается одно и то же.

eao197 ★★★★★
()
Ответ на: комментарий от eao197

Ok. А проблема всё таки не в стандарте - концептуально, расстояние модет иметь знак минус. Короче, лучше хрен сделаешь)

Deleted
()
Ответ на: комментарий от missxu

Принеси лучше «офф иссусле» из трекера clang-а.

anonymous
()
Ответ на: комментарий от eao197

Странный подход, конечно, но выглядит вполне себе рабочим.

Такой подход какой-то корифей из комитета на cppcon продвигал.

anonymous
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.