LINUX.ORG.RU

Почему компилятор не видит заинклюденный класс?

 , ,


0

4

Есть вот такой простой класс:

define FORMATTER_H

#include <QObject>

#include "../Editor.h"
#include "../EditorConfig.h"
#include "../EditorTextArea.h"

class Formatter : public QObject
{
  Q_OBJECT
public:
  explicit Formatter(QObject *parent = 0);

  void setEditor(Editor *iEditor); // <-- Здесь ошибка
  void setTextArea(EditorTextArea *iTextArea);

private:

  Editor editor;                   // <-- И здесь ошибка  
  EditorConfig *editorConfig;

  EditorTextArea *textArea;

};

#endif // FORMATTER_H

При его компиляции вылезают ошибки, что неизвестен класс Editor. Но я же его инклюдю, чего еще надо то? Ошибки следующие:
In file included from ../mytetra/src/libraries/wyedit/Editor.h:19:0,
                 from ../mytetra/src/views/record/MetaEditor.h:8,
                 from ../mytetra/src/views/recordTable/RecordTableScreen.cpp:12:
../mytetra/src/libraries/wyedit/formatters/Formatter.h:22:18: error: 'Editor' has not been declared
   void setEditor(Editor *iEditor);
                  ^
../mytetra/src/libraries/wyedit/formatters/Formatter.h:27:3: error: 'Editor' does not name a type
   Editor editor;
   ^

Пробовал не инклюдить, а прописывать предварительное описание класса через служебное слово class. А инклюд размещал не в заголовке, а в реализации. Проблема все равно остается.

Репозитарий:

https://github.com/xintrea/mytetra_dev/commits/editorModification

Коммит:

fc543909e727ee9dd4bccbeaa856512fbfae8f58


Как избавиться от этой ошибки?

ЗЫЖ После исправления, там скорее всего будут другие ошибки, потому что много чего переделал относительно предыдущего коммита. Но мне бы избавиться от той что в топике.

★★★★★

Последнее исправление: Xintrea (всего исправлений: 1)

Сделать в Formatter.h предварительное объявление Editor, Добавить include "../Editor.h" в Formatter.cpp и заменить

 Editor editor;                   // <-- И здесь ошибка  

на

 Editor * editor;
Vinick ★★
()
Ответ на: комментарий от Vinick

Исправил на указатель, эта часть компиляции прошла.

Но не смог «Сделать в Formatter.h предварительное объявление Editor, Добавить include »../Editor.h" в Formatter.cpp".

Не смог, потому что Formatter у меня является friend-классом для Editor. А для Friend-классов предварительное описание не прокатывает.

Коммит:

0cc340434af62d2612f61439dcbd4da248cca005

Ошибка:
../mytetra/src/libraries/wyedit/formatters/TypefaceFormatter.cpp
In file included from ../mytetra/src/libraries/wyedit/formatters/../Editor.h:20:0,
                 from ../mytetra/src/libraries/wyedit/formatters/Formatter.h:6,
                 from ../mytetra/src/libraries/wyedit/formatters/TypefaceFormatter.h:4,
                 from ../mytetra/src/libraries/wyedit/formatters/TypefaceFormatter.cpp:6:
../mytetra/src/libraries/wyedit/formatters/../formatters/PlacementFormatter.h:8:1: error: expected class-name before '{' token
 {
 ^
Makefile:24124: recipe for target 'build/TypefaceFormatter.o' failed

Xintrea ★★★★★
() автор топика
Ответ на: комментарий от i-rinat

У тебя в репозитории нет ни shared_ptr, ни unique_ptr. Без них же не удобно!

Я не дружу с умными указателями, не понимаю как они работают. И сейчас переводить большой проект на них не буду.

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

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

Ну посмотри напр. сюда (классический пример scoped-pointer'a рассматривается): http://habrahabr.ru/post/140222/

Меньше рутинного печатания будет, и при этом меньше ошибок.

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

А теперь вопрос: в каких случаях какие использовать?

Первый - если требуется совместное владение, второй - если нет.

Но вообще я плохо понимаю как можно писать на плюсах и при этом не осилить умные указатели.

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

QScopedPointer традиционно ставят в pimpl, чтобы убрать приватные кишки класса из hpp. QSharedPointer используют для согласования времён жизни объектов, написанных, например, разными разработчиками, которые иначе специально друг с другом это не согласовывали, чтобы захватив свой экземпляр ссылки, отложить момент разрушения экземпляра чужого объекта как минимум до момента, когда мы разрушимся сами. Так что если ты 1) делаешь pimpl то у тебя автоматически есть юзкейс для первого и 2) согласуешь работу других (подчинённых) программистов, то у тебя есть юзкейс для второго.

PS. На всякий случай, QSharedPointer и QScopedPointer -- аналоги shared_ptr и unique_ptr соответственно, которые не требуют cxx11 и идут в комплекте, если ты уже используешь Qt.

d_a ★★★★★
()
Последнее исправление: d_a (всего исправлений: 2)
Ответ на: комментарий от DarkEld3r

Первый - если требуется совместное владение, второй - если нет.

Совместное владение вместе с кем? Вместе с другим разрабочиком, или вместе с другим объектом?

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

Беда. Наверное, лучше тогда обратится к фундаментальной литературе по C++. pimpl -- костыль для контроля ABI классов. Приватные кишки -- всё что не является API класса. Юзкейс -- область применения.

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

Наверное, лучше тогда обратится к фундаментальной литературе по C++. pimpl — костыль для контроля ABI классов. Приватные кишки — всё что не является API класса.

Ну у меня несколько книжек по C++, самая вменяемая оказалась «Самоучитель C++» Г. Шилдта (А самый шлак - Подбельский). Ни в одной из них нет упоминания pimpl.


Юзкейс — область применения.

Это область применения переменной? Метода? Или вообще алгоритма?

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

Ни в одной из них нет упоминания pimpl.

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

private:
    struct PrivateData;
    QScopedPointer <PrivateData> m_d;

и в дальнейшем при разработке класса модифицировать только PrivateData, чтобы не менялся размер класса и не отваливались скомпилированные клиенты.

Это область применения переменной? Метода? Или вообще алгоритма?

Просто область применения, дословно.

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

и в дальнейшем при разработке класса модифицировать только PrivateData, чтобы не менялся размер класса и не отваливались скомпилированные клиенты.

Ну, в моем случае это не актуально.

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

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

Ну, в моем случае это не актуально.

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

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

Но как я понял, ты довольно активно приглашаешь к сотрудничеству в свой проект

Ага, и за пять лет не нашел ни одного разработчика. Думал, что проект поможет мне разобраться в C++. Но так как никого нет, то и общаться не с кем. Я только убедился, что C++ никогда не пойму, вот и весь профит.

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

Знакомо понятие «подсчёт ссылок»? shared_ptr хранит в куче не только сам объект, но и число ссылок на него. В конструкторе число увеличивается, в деструкторе уменьшается. Копирование вызывает конструктор копирования, а при выходе из зоны видимости вызывается деструктор.

Выглядит так, будто пишешь на языке со сборкой мусора.

i-rinat ★★★★★
()
Ответ на: комментарий от Xintrea

А, ну 5 лет для C++ не срок. А если на чистом самоподдуве, так и тем более. Так как многие вещи типа смарт-пойнтеров или, скажем абстрактных фабрик встанут на место только после первой попытки написать реюзабельный и расширяемый (i.e. библиотечного качества) код для других людей на этом языке.

d_a ★★★★★
()
Ответ на: комментарий от i-rinat

Знакомо понятие «подсчёт ссылок»? shared_ptr хранит в куче не только сам объект, но и число ссылок на него. В конструкторе число увеличивается, в деструкторе уменьшается. Копирование вызывает конструктор копирования, а при выходе из зоны видимости вызывается деструктор.

Это все понятно, но я смотрю на примеры, и мне становится страшно. В случае обычных указателей ты хотя бы знаешь, что есть некий тип, и ты нему обращаешься по указателю этого типа. И все.

А в случае этих умных поинтеров нужно какие-то дикие конструкции писать, что-то там с шаблонами колхозить чтобы хоть немного код упростить. Весь мозг нужно сломать, чтобы обратиться по указателю. Зачем так уродовать код? В плюсах жизнь и так не сладкая, а тут тебе еще синтаксического уродства полные штаны навалят.

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

Пожелай я включиться, меня остановили бы принятый стиль кода и коммиты в стиле «есть ещё ошибка, не собирается». Я понимаю, что предпочитаемое оформление — личное дело каждого, но хотя бы отступы надо делать одинаковыми (и не по одному пробелу!). Ломающие сборку правки в master не надо делать вообще никогда. Код должен собираться хотя бы на машине разработчика. Хочешь экспериментов — заведи новую ветку и развлекайся там.

i-rinat ★★★★★
()
Ответ на: комментарий от Xintrea

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

Зачем так уродовать код?

Вот, гляди: пример. В priv, который std::unique_ptr<Impl>, прячутся детали реализации. Инициализируется он через new, доступ к содержимому через ->, но удалением занимается unique_ptr. Эдакий указатель на стероидах.

Забыть удалить или удалить дважды тут не получится. И читателю кода ясно, что такую ошибку можно даже не искать.

В плюсах жизнь и так не сладкая

Так она потому и была не сладкая, что надо было делать работу, которую может делать компилятор.

мозг нужно сломать

Да, мозг придётся сломать.

i-rinat ★★★★★
()
Последнее исправление: i-rinat (всего исправлений: 2)
Ответ на: комментарий от Xintrea

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

Что за бред я читаю?

struct T
{
    void foo() {}
};
 
int main() {
    auto a1 = new T;
    a1->foo();

    auto a2 = make_shared<T>();
    a2->foo();

    auto a3 = make_unique<T>();
    a3->foo();
}
Где вот тут уродование кода? Qt под рукой нет, но более чем уверен, что с их смарт поинтересами будет ничуть не сложнее.

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

А, ну 5 лет для C++ не срок.

Это смотря для чего. Уж смартпоинтеры и прочие азы осилить - более чем достаточно времени. Конечно, при наличии желания и времени.

Не знаю кем и где работает автор, но похоже (по крайней мере, я на это надеюсь), что не С++ программистом. Работа в (нормальной) команде вправила бы мозги.

DarkEld3r ★★★★★
()
Ответ на: комментарий от i-rinat

Пожелай я включиться, меня остановили бы принятый стиль кода и коммиты в стиле «есть ещё ошибка, не собирается»

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

Посмотри периодичность коммитов. Есть периоды где раз в пару дней, потом дырки по месяцу, потом коммиты раз в полмесяца, потом вообще по три месяца без активности. В таком режиме ты вообще забываешь что вообще хотел сделать и как. Но у меня работа с программированием не связана, так что жаловаться на это не пристало.


Я понимаю, что предпочитаемое оформление — личное дело каждого, но хотя бы отступы надо делать одинаковыми (и не по одному пробелу!)

У меня два пробела (и тому есть логичное объяснение), и об этом написано в кодестайле проекта прямо на главной странице https://github.com/xintrea/mytetra_dev. В коде еще остались нетронутые куски с одним пробелом, но я их постепенно вычищаю.


Ломающие сборку правки в master не надо делать вообще никогда. Код должен собираться хотя бы на машине разработчика. Хочешь экспериментов — заведи новую ветку и развлекайся там.

Ну так у меня так и сделано. Есть master и experimental. В master попадает только рабочий код из experimental. И даже в experimental я уже давно не коммичу несобирающиеся сборки. Вот сейчас весь эксперимент идет в ветке editorModification. Чего еще надо то?

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

Ну так у меня так и сделано. Есть master и experimental.

А, так я на feature ветку и смотрел. Тогда всё хорошо.

и тому есть логичное объяснение

Про два пробела видел, но обоснования не нашёл. Не поделишься причиной? А то вон в ядре табы по 8 мест, ширина 80 мест (у тебя этого нет, похоже), и им нормально.

i-rinat ★★★★★
()
Ответ на: комментарий от DarkEld3r

Где вот тут уродование кода? Qt под рукой нет, но более чем уверен, что с их смарт поинтересами будет ничуть не сложнее.

Ну, с именем класса в одну букву - да, кажется просто. А как только сталкиваемся с реальностью, то видим:

std::shared_ptr<AnyObjectName> ptr = std::shared_ptr<AnyObjectName>(new AnyObjectName(param1, param2, param3));

Я считаю, что это за границами добра и зла. А потом мне говорят: так ты давай, шаблонами прикрывайся, ну например:
#define smartPtr(T) std::shared_ptr<T>

А потом ты легко можешь писать:
smartPtr(AnyObjectName) ptr=(smartPtr(AnyObjectName))(new AnyObjectName(param1, param2, param3));

Вот, видишь как красиво? Было 112 символов на присвоение, а стало 98! Это называется правильный код! Или ты, зажравшаяся скатина, хочешь попроще? - Да, хочу! - А вот тебе болт. В крестах такого не бывает.

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

Вот, видишь как красиво? Было 112 символов на присвоение, а стало 98!

auto ptr = std::make_shared<AnyObjectName>
(param1, param2, param3);
i-rinat ★★★★★
()
Ответ на: комментарий от i-rinat

Про два пробела видел, но обоснования не нашёл. Не поделишься причиной?

Единственная «золотая середина» размера отступа - это когда смещение вложенности происходит визуально на 45 градусов.

Оформление кода: Почему я выбрал для себя отступ в 2 пробела?

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

А как только сталкиваемся с реальностью, то видим:

А зачем так писать? Лучше во так:

auto ptr = std::make_shared<AnyObjectName>(param1, param2, param3);

А потом мне говорят: так ты давай, шаблонами прикрывайся, ну например:

И где там шаблон? Я вижу дефайн, а это макрос, а не шаблон. И я очень сомневаюсь, что кто-то такое будет советовать.

А вот тебе болт. В крестах такого не бывает.

В крестах, конечно, всё далеко не идеально и местами не слишком изящно, но то, что ты перечислил - это ерунда. И жизнь себе упростить можно, если уметь пользоваться языком. Но главное другое - изначально ты говорил:

Весь мозг нужно сломать, чтобы обратиться по указателю

И где тут проблемы «обращения по указателю»?

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

золотая середина

Утверждение спорное, но как я уже говорил, это личное дело каждого.

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

Как считаешь, auto уже можно пользоваться, или надо еще подождать пока стандарт распространится?

Xintrea ★★★★★
() автор топика
Ответ на: комментарий от i-rinat

pragma once

Второй раз вижу человека который использует #pragma once. Меня останавливает от её использования только малораспространенность. Как думаешь, почему эта директива не стала стандартом де-факто, а люди еще фигачат свои ifndef-гарды?

JANB
()
Ответ на: pragma once от JANB

Как думаешь, почему эта директива не стала стандартом де-факто, а люди еще фигачат свои ifndef-гарды?

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

Меня лично вымораживает то, что пользующиеся ifndef guard'ами забивают на уникальность идентификаторов.

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

Наверное, хотят, чтобы их код собирался под максимально возможным числом платформ.

Подозреваю, что зачастую тут просто инерция - «ведь pragma once нестандартное решение».

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