Как нам реорганизовать С++

Apr 24, 2024 12:47



Понадобилось мне давеча сделать нечто хитрое с помощью взаимно-рекурсивных классов.

Только для сиплюсплюсников )

work

Leave a comment

spamsink April 25 2024, 03:40:31 UTC
Так ведь это "глубокое вникание" и происходит сейчас, когда парсится non-template определение класса. Вот если при этом парсинге возникли ошибки, и все они - типа "incomplete class Foo" или являющиеся следствиями из них (если хочется упростить, то "если при этом парсинге возникли ошибки, и среди них есть типа "incomplete class Foo"), то
  • делаем список упомянутых неполных классов,
  • парсим "template<список со словом class перед каждым именем>",
  • парсим текущее определение класса, подменяя исходное имя класса на что-то уникальное,
  • если всё равно есть ошибки, выдаем полученные при первой итерации парсинга, иначе парсим using исходное_имя_класса = уникальное_имя<список>;
  • продолжаем парсить исходник далее

Reply

yatur April 25 2024, 05:12:15 UTC
Такая магия (которой и так много в С++) обычно ни к чему хорошему не приводит. Начиная с того, что класс неожиданно окажется темплейтом, а значит его методы должны быть видны при компиляции. Типа

// bar.h
class Bar { // implicit template
public:
void doSomething();
};

// bar.cpp
void Bar::doSomething() {... stuff... };

// usingBar.cpp
Bar b; // does not compile -- what are the template arguments?
Bar b; // OK
b.doSomething(); // the method is undefined

Далее, будут всякие приколы с template parameter inference:

class Bar { // implicit template on Foo
public:
Bar(const Foo& theFoo) {...}
};

class Foo {
public:
Foo(int arg) {...}
};

Bar bla(42); // if not template, a temporary Foo is constructed from 42
// if template, we are dealing with Bar now

Reply

spamsink April 25 2024, 05:27:40 UTC
С какого бы рожна class Bar { public: void doSomething(); }; вдруг стал implicit template, если его декларация и так отлично компилируется? Вы, похоже, так и не поняли, чего именно я хочу.

Reply

yatur April 25 2024, 05:36:50 UTC
Ну зачем сразу выбирать вариант "собеседник ни черта не понял" :)
Отвечаю по порядку.

> С какого бы рожна class Bar вдруг стал implicit template,

Я писал в предположении, что class Bar содержит рекурсивные определения, которые, по вашему механизму, превращают его в неявный template. Я их просто не вписал, посчитав, что комментария достаточно. Ну вот допутим есть в нем вот это

Foo foo1, foo2;

class Bar { // implicit template
public:
void doSomething();
Foo foo1, foo2;
};

Раз Bar теперь template, то его методы обязаны быть видны при компиляции всех использований. А раньше этого не было.

> Вы, похоже, так и не поняли, чего именно я хочу.

Я думаю, что понял общую идею. Если при компиляции класса Bar возникают ошибки, проистекающее от того, что класс Foo не определен, превращаем Bar в неявный темплейт Bar. Дальше будет много интересного, но это уже другая история :) И, кстати, вряд ли это позволит превратить Foo foo1, foo2; в vector, а если позволит, то ой. Но это уже третья история :)

Reply

spamsink April 25 2024, 05:46:48 UTC
Не вижу проблемы, поскольку

#include < iostream>
#include < vector>

struct Foo;

template < typename T> struct BarT { T foo1, foo2; void doSomething(); }; // !!!
using Bar = BarT< Foo>; // !!!

struct Foo { std::vector< Bar> bar; };

template< > void BarT< Foo>::doSomething() { std::cout << "Something\n"; }
// Демонстрация, что компилируется и работает
int main() {
Bar bar;
bar.doSomething();
}

компилируется и работает. Всего-то и надо, что при превращении класса в template или требовать, чтобы тела outlined методов определялись после доопределения необходимых классов, или сохранять их синтаксические деревья до этого момента, и только после этого компилировать.

Reply

yatur April 25 2024, 06:00:34 UTC
Ну... Во-первых, с настоящими темплейтами популярные компиляторы так не делает, значит это либо нетривиально, либо очень медленно. Они требуют, чтобы тело метода было в том же логическом compilation unit (т.е. исходник + инклюды), что и использование.

Во-вторых, out of line метод может быть определен в библиотеке, распространяемой в виде двоичного файла + хедеры, и его синтаксическое дерево будет недоступно. Чтобы оно стало доступно, придется перекомпилировать все библиотеки новым компилятором. Это резко замедлит adoption.

В-третьих, начнутся забавные проблемы с shared objects. Сейчас уже слишком поздно, чтобы я мог подробно описать какие, но они точно будут :D

Сегодня обычные классы и темплейты живут как бы в разных мирах и подчиняются абсолютно разным правилам. Это в каком-то смысле два разных языка: две разные системы представления двоичного кода, две разные системы диагностики ошибок, и т.п. Неявный перевод классов из обычной системы в мир темплейтов возможен, но это будет еще одна мина.

Reply

spamsink April 25 2024, 07:10:20 UTC
О том и речь, что это получается не "настоящий" темплейт, а всегда мгновенно специализированный, причем с единственной специализацией, что существенно упрощает дело.

Reply

yatur April 25 2024, 07:41:56 UTC
На самом деле, можно не мучаться, а просто вынести методы Foo, которые использовуют Bar, за пределы класса Foo. И никакие темплейты не нужны.

struct Bar;

struct Foo {
std::vector bar;
void useBar();
};

struct Bar {
Foo foo1, foo2;
};

inline void Foo::useBar() {
bar[0].foo1 = Foo();
}

Reply

spamsink April 25 2024, 08:19:31 UTC
Если этих useBar() - приличное количество, и у каждой по несколько параметров, это вынесение будет бОльшим извратом, чем объявление Bar через template.

Reply

rezkiy April 25 2024, 16:05:24 UTC
Я не об этом. Лукап в месте дефинишена и в месте инстанциации в общем случае приводит к разным результатам. Вы меняете array на vector, и у вас из-за этого меняется семантика. Я немного сбивчиво пишу, это проще объяснить многословно, размахивая руками.

Reply

spamsink April 25 2024, 17:07:40 UTC


Изменение семантики проще всего объяснить на примере. Если его удастся создать, конечно.

Reply


Leave a comment

Up