ФорумПрограммированиеБольше языковC/C++ и C# → Вложенный файл не видит файла который был включенным раньше(?)

Вложенный файл не видит файла который был включенным раньше(?)

  • Rotten

    Сообщения: 2243 Репутация: N Группа: Адекваты

    Spritz Май 6, 2011, 2:23 п.п.

    Такая вот ситуация. Есть несколько файлов(классов) которые инклюдятся в определенном из них по необходимости.
    (Программа - на языке С++, не смотря на то что сорцы предоставленные здесь - на гарпе, т.к. варианта обернуть его в с++ я не нашел из предоставленных).
    А именно…
    1. Файл KeyBoard.cpp. Он инклюдится в винМэйн методе первым. Все что инклюдится дальше - то все через него(в винмэйне из этой все игрархии больше ничего нет). В этом файле все что нас интересует - это включенный Controller.cpp…

    #include "stdafx.h"
    #include "Controller.cpp"


    class KeyBoard{


    public:
    KeyBoard(){

    };
    private:
    Controller controller;

    void allocateButtons(){

    std::vector<KeyBoardChar> nums = this->controller.getNums();

    };

    LangLayout getDefaultLangLayout() {
           return this->controller.getDefaultLangLayout();
       }
    };



    2. Идем дальше… Файл Controller.cpp. Обращаем внимание на включенные Model.cpр и LangLayout.cpp, т.к. они играют тут ключевую роль.
    Так же в этом классе упоминается класс KeyBoardChar(в нем то вся какраз и соль) который включен Model.cpp. Но на этом этапе пока все тоже ок…

    #include "stdafx.h"
    #include "Model.cpp"
    #include "LangLayout.cpp"


    class Controller{


    public:
    Controller(){

    };

       std::vector<KeyBoardChar> getNums()
       {
    Model *m = new Model();
           return m->NUMS_LAYOUT;
       };


    LangLayout getDefaultLangLayout() {
           return defaultLangLayout;
       };

    private:

    LangLayout defaultLangLayout,
               secondaryLayout,
               specialCharsLayout;

    Model model;

    void allocateButtons(){
    OutputDebugString(L"allocateButtons()…");
    };

    };


    3. класс Model.cpp. Тут особо ничего нет, опять как и всюду, ключевой момент - последовательные инклюды.. и вот он, включенный KeyBoardChar.cpp…
    Все, по сути я так понимаю дальше все включаемые файлы должны его видеть…


    #include "stdafx.h"
    #include <vector>
    #include "KeyBoardChar.cpp"


    class Model{

    public:

       std::vector<KeyBoardChar> NUMS_LAYOUT;// (12);
       
    };



    4. Вернемся к файлу Controller.cpp, так как он кроме Model.cpp содержит включение еще и LangLayout.cpp, который подключается позже.

    #include "stdafx.h"
    #include <vector>

    class LangLayout{


    public:

    LangLayout() {
       
       };

    LangLayout(unsigned int aid) {
           this->setID(aid);
           //this.layout = new Vector();
       };

       unsigned int getID() {
           return this->id;
       };

       void setID(unsigned int aid) {
           this->id = aid;
       };





    private:

      KeyBoardChar b;
       unsigned int id;

    };


    И вот в этом файле зарыта проблема так как компилятор на нем крешает. Если ему явно не объявить включение KeyBoardChar.срр(а нам и ненужно, ибо как было сказано - он включается раньше - в Model.cpp), то компилятор выдает:
    C:\test1\LangLayout.cpp(58) : error C2146: syntax error : missing ';' before identifier 'b'
    C:\test1\LangLayout.cpp(58) : error C2501: 'KeyBoardChar' : missing storage-class or type specifiers
    C:\test1\LangLayout.cpp(58) : error C2501: 'b' : missing storage-class or type specifiers

    (Там правда строчка не 58 потомучто я тут много убрал из файла закомменченого, но суть одна и та же - крешает в конце где обьявлено поле приватным "  KeyBoardChar b;")

    А вот если я обьявлю инклюд фала KeyBoardChar.срр - то как и предполагалось, выдаст " 'KeyBoardChar' : 'class' type redefinition "…

    В чем же проблема? Возможно нужно какието нэймспейсы поопределять чтоли. Я хэдеров кстати не юзаю(предполагаю определять методы/поля в классах сразу).

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



    Спустя 41 сек.
    Алсо, я пробовал ставить #pragma once, но где бы его не ставил, все одно и тоже. Похоже не в том дело…
    Суть вся в том что, какие свои классы в LangLayout.cpp я бы не вставлял(не важно, KeyBoardChar, Model, Controller) - он на них всегда матерится… а вот на свои встроенные - все пойдет на ура…
  • Rotten

    Сообщения: 2243 Репутация: N Группа: Адекваты

    Spritz Май 6, 2011, 6:01 п.п., спустя 3 часа 37 минут 59 секунд

    Я решил проблему, вроде как. Но мне кажется это далеко не лучшее решение.
    В классе Controller.cpp где обьявлені инкльюді я добавил
    [text]
    #define KBC 1
    [/text]

    И наконец в классе LangLayout.cpp я проверяю…
    [text]
    #ifndef KBC
    #include "KeyBoardChar.cpp"
    #endif
    [/text]

    И учитывая то что условие ifndef KBC не выполняется(т.к. KBC == defined) то KeyBoardChar.cpp повторно не включается, а компилятор ничего против не имеет.

    Но так объявлять часто препроцессорные переменные - дело муторное, должно быть решение пооптимальнее…
  • vasa_c

    Сообщения: 3131 Репутация: N Группа: в ухо

    Spritz Май 7, 2011, 4:39 п.п., спустя 22 часа 38 минут 5 секунд

    Зачем включать cpp'шные файлы? Обычно только H включают, а cpp просто линкуют после компиляции.
  • Rotten

    Сообщения: 2243 Репутация: N Группа: Адекваты

    Spritz Май 8, 2011, 2:34 п.п., спустя 21 час 54 минуты 26 секунд

    Зачем включать cpp'шные файлы?

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

    Пробовал кстати и хидер сделать и подключить его, но от проблемы я не избавился…

    По сути в хидерах - нужно объявлять (но не определять) классы(в моем случае, а в большинстве - глобальные переменные и прочее) и только в cpp файлах их определять/реализовать, насколько я понимаю.
    В хидерах, в объявлениях всеравно будут присутствовать типы классов/интерфейсов(в параметрах функций либо в качестве возвращаемых методом типов) которые надо подключать в разнообразном порядке. Тоесть по сути проблема остается той же…

    Есть класс которому нужен другой класс, вот он и его подключает. Другой клас в свою очередь юзает третий класс, и так далее.. Тоесть все по мере необходимости. Так предусмотрена архитектура.
    Может в срр по-другому както..
  • Rotten

    Сообщения: 2243 Репутация: N Группа: Адекваты

    Spritz Май 8, 2011, 2:39 п.п., спустя 5 минут 32 секунды

    Можно в принципе захуячить все классы в одном файле(а их объявления - в хидерах, соответственно). Возможно так изначально на срр и предусмотрено (ведь на простом си - процедурное программирование, там все функции могут быть как в пхп - одном файле, а с++ же много унаследовал от си)…

    Но думаю этот подход далеко не идеален и в заблуждение введет если кода будет слишком дохуя..
  • Nyaah

    Сообщения: 574 Репутация: N Группа: Джедаи

    Spritz Май 9, 2011, 9:31 п.п., спустя 1 день 6 часов 51 минуту

    В заголовочных файлах указываются объявления функций/классов, делаются дефайны, для оптимизации запихивают туда же тела инлайн функций, также указываются директивы прекомпилятора, в сурс файлах соответственно реализация функций и классов плюс в начале файла инклуд хедера для текущего сурс файла.
    Для того чтобы не было ошибки редефайна класса/фукции (возникает, если сделать инклуд из разных файлов проекта), в заголовочный файл в начало либо вешают директиву
    #pragma once
    либо обёртывают код директивами
    #ifndef _YOUR_CLASS_OR_LIB_NAME_
    #define _YOUR_CLASS_OR_LIB_NAME_

    // here other file content

    #endif /* _YOUR_CLASS_OR_LIB_NAME_ */


    в итоге обычно это выглыдит примерно так:
    Файл TestClass.h:
    #ifndef _OUUE_TEST_CLASS_
    #define _OUUE_TEST_CLASS_

    namespace ouue {

    class TestClass
    {
    private:
    int _id;
    public:
    TestClass(int id = 0);
    TestClass(const TestClass& source);
    virtual ~TestClass()
    {};
    int getId() const;
    }; /* class TestClass */

    } /* namespace ouue */

    #endif /* _OUUE_TEST_CLASS_ */


    Файл TestClass.cpp:
    #include "TestClass.h"

    namespace ouue {

    TestClass::TestClass(int id) : _id(id)
    {}

    TestClass(const TestClass& source)
    {
    _id = source.getId();
    }

    int TestClass::getId() const
    {
    return _id;
    }

    } /* namespace ouue */

    #endif /* _TEST_CLASS_ */
    Если где-то нужен класс TestClass, просто в хедере пишем #include "TestClass.cpp", и все ок.

    P.S. Можно конечно и cpp классы инклудить и в один файл все объявления запихивать, но ты и сам потом запутаешься, и другие люди потом, читая твой код, будут сильно материться, и компилятор будет возможно непонятные ошибки выдавать, и оптимизатор хз как работать.
    Work, buy, consume, die
  • Rotten

    Сообщения: 2243 Репутация: N Группа: Адекваты

    Spritz Май 9, 2011, 10:10 п.п., спустя 39 минут 18 секунд

    Nyaah, да, я вроде как сам понял: в хедерах теперь обьявлены у меня классы, интерфейсы, обьявления констант и т.д.
    А клиент-файл(тоесть тот который первым создает инстансы одного из обьявленных классов которые архитектурно взаимодействуют между другими обьявленными) - должен быть срр-шным…

    Правда я вот в хедерах не только обявления классов храню - а сразу реализацию чтобы не плодить на каждый хедер срр-шники.

    Я кстати пробовал как ты и говорил(касательно ifdef), похоже что это и есть единственное решение.

    Да, и кстати, у тебя в примере TestClass.cpp - закрытый endif без открытого #ifdef/ifndef…

    Спасибо за внимание)…
  • Nyaah

    Сообщения: 574 Репутация: N Группа: Джедаи

    Spritz Май 9, 2011, 10:46 п.п., спустя 35 минут 35 секунд

    да пофик на ендиф, его не должно быть =)
    А по поводу кода в хедерах всетаки лучшей практикой считается разделять описание и реализацию, тогда для того чтобы увидеть интерфейс класса, просто открываешь заголовочный файл и видишь его, без лишнего кода. Ещё у компиляторов есть опция, благодоря которой можно заставить все методы объявленные в хедерах сделать инлайн (-Ob2), тогда могут посыпаться баги при наследовании, использовании каллбеков и т.п. Если твою либу или код юзают в стороннем проекте, а там ленивые программисты, которые не любят писать inline где нужно, а именно используют такой подход, то будет нехорошо =)
    Спустя 254 сек.
    и не единственное решение #ifndef - #endif, можно ещё #pragma once писать в начале заголовочного файла, эта директива говорит компилятору, что код файла должен включаться только раз
    Work, buy, consume, die
  • Rotten

    Сообщения: 2243 Репутация: N Группа: Адекваты

    Spritz Июнь 17, 2011, 2:43 п.п., спустя 38 дней 15 часов 57 минут

    Такой еще вопрос…
    Зачем включать cpp'шные файлы? Обычно только H включают, а cpp просто линкуют после компиляции.

    Тоесть при написании кода мы только хедеры подключаем а компилятор сам раздупляет что нужно подключить срр-шные файлы по имени файлов?
    Тоесть вручную мы срр-шки не подключаем, только хедеры…

Пожалуйста, авторизуйтесь, чтобы написать комментарий!