Перейти к содержанию
    

Плавный переход C -> C++ под МК

9 minutes ago, EdgeAligned said:

Поскольку пишу только для себя и не предполагаю, что моя писанина вообще будет кому-то нужна

Золотые слова! ))

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

56 минут назад, Arlleex сказал:

Если бы была возможность дробить определение класса (как, например, namespace), и выносить интерфейсную часть в хедер, а все темные дела в .cpp, то был бы такой же эффект. Но так сделать нельзя. Ослиные уши в виде прототипов приватных функций, да и сами приватные данные, будут видны в определении класса, т.е. в хедере.

Здесь можно вспомнить про идиому pimpl, которая, кроме припрятывания приватных дел в cpp-файл, теоретически позволяет уменьшить число перекомпиляций (можно много что менять в реализации без изменений в *.h-файле.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

1 час назад, EdgeAligned сказал:

Впрочем, лично я не заморачиваюсь постройкой того, на что комитет по ++ просто забил.

Можно, конечно, забить. Но это не мой путь. Мой путь - создать свое видение "идеального" кода на плюсах, с его тонкостями и нюансами. И чтобы это выработать, нужно пролопатить 100500 вариантов от множества разработчиков, послушать их опыт и рекомендации. Опять же - я не по работе щас пекусь - там я просто прогаю на C++ в Си стиле - нет трехэтажных абстракций ради абстракций. А в свободное время мне интересно поковыряться с синтаксическими штуками, дабы оставить, в конечном счете, только нужное, чтобы если это написано - значит это нужно, а не потому что некий костыль надо залатать. Пока что да, костылей очень много - в частности, недавнее упрятывание дефолтного конструктора в приват и обозначение его как = delete. Но с другой стороны, из-за того, что в плюсах наблюдается тенденция реализовывать абсолютно одинаковые по замыслу вещи совсем разными подходами, становится возможным отсечь максимально ущербные варианты и оставить лучшее))

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

1 час назад, EdgeAligned сказал:

Proteted - это область видимости в первом уровне наследования.

я знаю что такое протектед и для чего он используется. Поэтому я его не использую ни для членов, ни для функций. Это источник проблем. По сути это тот же паблик, только среди ограниченного круга. Это ваш личный холодильник на этаже в общежитии/комуналке. Кроме Вас, к нему имеют доступ все жители общяги, но ни кто "чужой" не сможет туда попасть )).

 

1 час назад, EdgeAligned сказал:

Если же писать "для всех", то в идеале должны быть закрытые библиотеки в виде файлов типа .a

Если писать для всех закрытые библиотеки в виде файлов типа .a, то да. А если писать для всех срр или работать в команде.... хороший пример "для всех" всякие ротос (кернел, фриртос, сцм), Qt, Qwt, LVGL (ой, это же не всё ++), boost, .... и все с исходниками без.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

35 минут назад, AHTOXA сказал:

Здесь можно вспомнить про идиому pimpl, которая, кроме припрятывания приватных дел в cpp-файл, теоретически позволяет уменьшить число перекомпиляций (можно много что менять в реализации без изменений в *.h-файле.

Ехех) Если б еще я детально понимал работу умных указателей)) Поэтому пока что оставил в заметках)

 

P.S. Примерно в такое сейчас вырисовывается интерфейс к драйверу дисплейчика (еще пяток функций будет, но это уже не суть важно)

namespace OLED {
  class Hardware final {
    public:
      static void init();
      
      static bool sendCommand(u8 cmd) {
        return send(COMMAND, cmd);
      }
      
      static bool sendCommand(u8 cmd1, u8 cmd2) {
        return send(COMMAND, cmd1, cmd2);
      }
      
      static bool sendCommand(u8 cmd1, u8 cmd2, u8 cmd3) {
        return send(COMMAND, cmd1, cmd2, cmd3);
      }
      
      static bool sendCommand(u8 const cmdBuf[], u32 size) {
        return send(COMMAND, cmdBuf, size);
      }
      
      static bool sendData(u8 data) {
        return send(DATA, data);
      }
      
      static bool sendData(u8 data1, u8 data2) {
        return send(DATA, data1, data2);
      }
      
      static bool sendData(u8 data1, u8 data2, u8 data3) {
        return send(DATA, data1, data2, data3);
      }
      
      static bool sendData(u8 const dataBuf[], u32 size) {
        return send(DATA, dataBuf, size);
      }
    
    private:
      enum TransferType : bool {
        COMMAND = 0,
        DATA
      };
      
      
      Hardware() = delete;
      
      static bool send(TransferType dc, u8 arg);
      static bool send(TransferType dc, u8 arg1, u8 arg2);
      static bool send(TransferType dc, u8 arg1, u8 arg2, u8 arg3);
      static bool send(TransferType dc, u8 const argBuf[], u32 size);
  };
}


Сразу видно, что есть функция init(), которую клиентский код вызовет для инициализации железа. Также видны (в публичной интерфейсной части) inline-функции транзакций, с различными форматами. В приват запихнуты вещи, которые "не хотелось бы" отдавать в API напрямую. Сначала всякие энумы, потом уже функции. Приватные функции реализуются уже в *.cpp, поэтому пока что соблюдается идеология скрытия реализации подальше от глаз и потенциального изменения чего-либо при раздельной компиляции в статическую либу.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

19 minutes ago, Arlleex said:

римерно в такое сейчас вырисовывается интерфейс к драйверу дисплейчика (еще пяток функций будет, но это уже не суть важно)

А я бы так переделал, убрав в cpp всю реализацию и "лишние" методы:

namespace OLED {
  class Hardware final {
      Hardware() = delete;

    public:
      static void init();
      static bool sendCommand(u8 cmd);
      static bool sendCommand(u8 cmd1, u8 cmd2);
      static bool sendCommand(u8 cmd1, u8 cmd2, u8 cmd3);
      static bool sendCommand(u8 const cmdBuf[], u32 size);
      static bool sendData(u8 data);
      static bool sendData(u8 data1, u8 data2);
      static bool sendData(u8 data1, u8 data2, u8 data3);
      static bool sendData(u8 const dataBuf[], u32 size);
  };
}

 

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

12 минут назад, Forger сказал:

А я бы так переделал, убрав в cpp всю реализацию и "лишние" методы:

Тут я об этом думал. Но поскольку содержимое этих SendCommand() попарно одинаково с соответствующим SendData(), не очень хотелось дублировать код в реализации. Поэтому запихнул вызов в inline-функции класса. Он, вроде, должен сразу подставить вызов нужного send(), а в образе прошивки не будет 8 разных функций. Будет 4 перегрузки send(). Я тут еще подумаю, как оно будет попрактичней.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

2 minutes ago, Arlleex said:

Тут я об этом думал. Но поскольку содержимое этих SendCommand() попарно одинаково с соответствующим SendData(), не очень хотелось дублировать код в реализации. Поэтому запихнул вызов в inline-функции класса.

А вот тут не стоит переживать, компиляторы нынче умные и довольно умело оптимизируют код ))

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

4 minutes ago, Arlleex said:

Он, вроде, должен сразу подставить вызов нужного send(), а в образе прошивки не будет 8 разных функций.

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

1937838630_.jpg.96d69634fcb90e22756c01697ae9ff04.jpg

 

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

3 минуты назад, Forger сказал:

А вот тут не стоит переживать, компиляторы нынче умные и довольно умело оптимизируют код ))

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

Ну тогда тут класс, в общем-то, не нужен. Достаточно заменить class на namespace и иметь просто именованный набор API-функций.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

2 minutes ago, Arlleex said:

Достаточно заменить class на namespace и иметь просто именованный набор API-функций.

Ну вообще то да, но именно в данном случае ))

 

 

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

enum Command {
  SPI_SEND_FLASH,
  SPI_SEND_RAM,
  SPI_SEND,
  SPI_FILL,
  SET_PIN,
  RESET_PIN,
  ...
};

template<Command>
struct Descriptor;

template<>
struct Descriptor<SPI_SEND_FLASH> {
  Command cmd  :  3;
  bool    dc   :  1;
  u32     addr : 19,
          size :  9;
} __attribute__((aligned(4)));

template<>
struct Descriptor<SPI_SEND_RAM> {
  Command cmd  :  3;
  bool    dc   :  1;
  u32     addr : 19,
          size :  9;
} __attribute__((aligned(4)));

template<>
struct Descriptor<SPI_SEND> {
  Command cmd  : 3;
  bool    dc   : 1;
  u32     size : 2;
  u8      byte[3];
} __attribute__((aligned(4)));

template<>
struct Descriptor<SPI_FILL> {
  Command cmd  :  3;
  bool    dc   :  1;
  u32     size : 10;
  u8      byte;
} __attribute__((aligned(4)));

template<>
struct Descriptor<SET_PIN> {
  Command cmd : 3;
  Pin     pin : 1;
} __attribute__((aligned(4)));

template<>
struct Descriptor<RESET_PIN> {
  Command cmd : 3;
  Pin     pin : 1;
} __attribute__((aligned(4)));

...


Вот есть у меня шаблон структурок, которые уже правильно "упакованы" по битам. Идея в том, что все эти структуры занимают в памяти ровно 4 байта (1 слово), и мой "планировщик" заданий работает с такими вот дескрипторами. В конечном счете, потом будет создан пул в памяти из таких вот дескрипторов, которые будут вычитываться драйвером и отдаваться на исполнение в зависимости от содержимого поля cmd. А клиентский код пишет в очередь заданий и не парится о готовности их завершения. Этакий драйвер с асинхронным неблокирующим API. Не суть, просто чтобы было понимание.

У всех без исключения специализаций структур первым полем идет поле команды, оно занимает 3 бита. Я хотел бы, чтобы при объявлении конкретного объекта, например,

Descriptor<SPI_SEND> desc;

в его поле cmd уже "автоматически" неявно попадало число SPI_SEND из перечислителя. Полагаю, что никаких вариантов, кроме явного определения абсолютно одинаковых конструкторов в каждой из специализаций - нет?

Думал, что в "основном" шаблоне можно было бы описать "единый" конструктор по-умолчанию, но это приводит к тому, что элемент cmd нужно перетаскивать в тело шаблона структуры, а специализации заменить на наследование. Но отделяя/забирая cmd из других структур, теряется гарантия, что все эти структуры останутся равны 4 байтам, т.к. при наследовании классов их члены будут иметь раздельно адресуемое положение. Битовое поле не адресуется, поэтому под cmd в шаблоне выделился бы какой-то объем отдельных байтов, что не годится.

Всем структурам по конструктору, получается? Или можно, все-таки, каким-то образом сказать компилятору, чтобы представлял содержимое потомков как "синтаксическое продолжение" к данным класса-родителя? Т.е. чтобы записать

template<Command>
struct DescriptorHeader {
  Command cmd : 3;
};

struct Descriptor_SPI_SEND : public DescriptorHeader {
  bool dc   :  1;
  u32  addr : 19,
       size :  9;
} __attribute__((aligned(4)));

и компилятор разместил cmd в первых трех битах машинного слова, в котором будет храниться объект типа Descriptor_SPI_SEND.

P.S. Посморел что сгенерировал компилятор - хмм... он при наследовании действительно "прилепил" cmd к тому же слову, что и остальные поля дочернего класса. Это хорошо, но... надежно ли?

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

3 часа назад, razrab83 сказал:

я его не использую ни для членов, ни для функций.

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

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Да, а для создания подобия интерфейса можно воспользоваться наследованием чистого виртуального классса, вот так:

class A_Interface {
public:
	virtual void Foo1() = 0;
	virtual void Foo2(int x) = 0;
};

class A: public A_Interface {
public:
	void Foo1() override
	{

	}

	void Foo2(int x) override
	{

	}

private:
	int a, b, c;
	void Priv()
	{

	}
};

Однако, и тут есть бочка дёгтя в ложке меда: статические методы не могут быть виртуальными и переопределены при наследовании.

Именно поэтому я в принципе то не заморачиваюсь. Если код реализации объемный, я выношу его из описания класса, а в классе оставляю только определения.
И еще одна жопа кроется в том, что все инклюды, которые вы подключили в конкретный .hpp, становятся неявно видимыми везде. Тоже неустраненное наследство от Си, на которое все забили.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

37 minutes ago, EdgeAligned said:

И еще одна жопа кроется в том, что все инклюды, которые вы подключили в конкретный .hpp, становятся неявно видимыми везде.

Так не надо туда подключать лишние инклуды ))

Поэтому в hpp кладу только те инклуды, который нужны для работы препроцессора на этот hpp файл, а все остальные инклуды - в соотв. cpp файл

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Так вот ведь, вроде бы решение шикарное, но как только сталкиваетесь с шаблонами, это шикарное решение разбивается о суровую действительность недоработок языка 🙂

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

вообще, я разочаровался в ++, он ущербный какой-то, мусорка из ненужного старого и недоделанного нового.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Присоединяйтесь к обсуждению

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

Гость
К сожалению, ваш контент содержит запрещённые слова. Пожалуйста, отредактируйте контент, чтобы удалить выделенные ниже слова.
Ответить в этой теме...

×   Вставлено с форматированием.   Вставить как обычный текст

  Разрешено использовать не более 75 эмодзи.

×   Ваша ссылка была автоматически встроена.   Отображать как обычную ссылку

×   Ваш предыдущий контент был восстановлен.   Очистить редактор

×   Вы не можете вставлять изображения напрямую. Загружайте или вставляйте изображения по ссылке.

×
×
  • Создать...