LINUX.ORG.RU

Как заставить подобный код НЕ компилироваться?

 ,


1

4

Всем привет, сделал такой код, ожидал, что выдаст ошибку компилции, но все собралось

#include <iostream>


class Test
{
public:
//  static void test()
//  {
//   some action
// }
};


template<class T>
class ITest
{
public:
  void test()
  {
    T::test();
  }

};

typedef ITest<Test> TTest;

int main()
{
  TTest test;
  return 0;
}

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

  typdef ImplClass alias;

  ...
  alias::Do();

где методы ImplClass - статические функции.

Появилось желание немного изменить концепцию и заставить компилятор проверять реализацию на соответствие интерфейсу. Но пока не выходит...

Поэтому у меня два вопроса: почему это компилится? как можно сделать то чего я хочу иначе?

★★★★★

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

А если в коде вызвать TTest::test()?

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

Есть способ гораздо проще - если у тебя есть класс Test и железки A и B, то общий код кладется в Test.h и Test.cpp, а реализации отличающихся методов в TestA.cpp и TestB.cpp, в сборку включается только файл для А или для В. А шаблоны нужны если ты хочешь обе реализации использовать в одной кодовой базе.

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

Ну это придется, для каждого метода прописывать. Так что как-то не очень.

Dudraug ★★★★★
() автор топика

Добавить test.test(); в main?

trash1.cpp:15:8: error: no member named 'test' in 'Test'
    T::test();
       ^
trash1.cpp:25:8: note: in instantiation of member function 'ITest<Test>::test' requested here
  test.test();
       ^
1 error generated.

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

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

Не, ну это понятно, что тут будет ошибка=)

Я скорее хотел бы проверку на соответствие интерфейсу. Например если бы можно было наследоваться и использовать виртуальные функции. То мы просто делаем ITest с чисто виртуальными функциями. А Test - реализация. Если у реализации хотя бы один метод не переопределен, то ошибка компиляции при попытке создания экземпляра.

Я хотел ошибку компиляции без вызова методов.

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

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

Хм, да уж. Ну понятно=)

Dudraug ★★★★★
() автор топика

1. C++ на микроконтроллерах - ССЗБ. Если так уж хочется ООП - подумай о Аде.

2. Задефайнь ключевое слово template в обращение по нулевому адресу.

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

И еще - подумай о реализации наследования через нейм-спейсы. Скорее всего для микроконтроллеров самое оно.

cvv ★★★★★
()
-  void test()
+  virtual void test()

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

xaizek ★★★★★
()

Можно заюзать концепты, в gcc они уже есть. Стильно, модно, молодёжно, но в будущем могут слегка поломать совместимость.

Esper
()
#include <iostream>

template<typename T> concept bool Test = requires (T t) {
    t.test();
};

class TTest {
public:
    /*
    void test() {
        // some action
    }
    */
};

int main()
{
  Test test = TTest{};
}
$ g++ test.cpp -fconcepts
test.cpp: In function ‘int main()’:
test.cpp:18:21: error: deduced initializer does not satisfy placeholder constraints
   Test test = TTest{};
                     ^
test.cpp:3:35: note: within ‘template<class T> concept const bool Test<T> [with T = TTest]’
 template<typename T> concept bool Test = requires (T t) {
                                   ^~~~
test.cpp:3:35: note:     with ‘TTest t’
test.cpp:3:35: note: the required expression ‘t.test()’ would be ill-formed
Esper
()

sfinae + enable_if сделают то, что ты просишь

template <class T>
struct has_test_method {
    template <class C> static constexpr ::std::true_type check(decltype (&C::test));
    template <class> static constexpr ::std::false_type check(...);
    static constexpr bool value = ::std::is_same<::std::true_type, decltype(check<T>(nullptr))>::value;
};

template <class T> constexpr bool has_test_method_v = has_test_method<T>::value;

//........................................

template<class T, typename = std::enable_if_t<has_test_method_v<T>>>
class ITest
{
public:
  void test()
  {
    T::test();
  }

};

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

Точно не помню почему это компилируется

ISO/IEC 14882:2014(E) 14.7.1 Implicit instantiation § 2

Unless a member of a class template or a member template has been explicitly instantiated or explicitly specialized, the specialization of the member is implicitly instantiated when the specialization is referenced in a context that requires the member definition to exist

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

Блин, походу я зря обозвал класс Test, я просто вообще в отдельном файле тестировал как оно работать будет. Никакого отношения к тестам код иметь не будет. В конечном классе может быть от 1 до 100500 методов. И таких классов и интерфейсов может быть овер 9000. По факту Test - это любой железячно-специфичный класс, который предоставляет реализацию для управления определеными компонентами, подсистемами. Например функции записи/чтения в нужные регистры.

Поэтому проверять наличие метода - это не то что я хочу. Если методов в интерфейсе(и реализации) 20, а есть еще один интерфейс с 10, то мне на каждый метод писать sfinae?

Я понимаю, что я немного неверно задал вопрос. Но это не то что я хочу=)

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

Блин, вот концепты смотрятся интересно. Но компилятор gcc, да и нестандартных решений не хотелось бы. Хотя на первый взгляд красота.

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

Это не тесты=) Это я имя выбрал неудачное. По факту интерфейсов может быть много (port, usb, leds). Но на каждом конкретном микроконтроллерае нужна уже разная имплементация (писать/читать то в разные регистры надо), поэтому Test - железная имплементация например usb на конкретной железке, а ITest - общий интерфейс.

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

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

По факту интерфейсов может быть много (port, usb, leds).

Так может привели бы один пример более-менее приближенный к реальности. Пока же складывается впечатление, что вам нужно смотреть в сторону CRTP и делать что-то вроде:

template<typename LEDS_IMPL>
class leds : protected LEDS_IMPL {
public:
  void turn_on() { this->actual_turn_on(); }
  ...
};
...
template<typename LEDS_IMPL>
void turn_leds_on(leds<LEDS_IMPL> & l) { l.turn_on(); }
...
Потом для одной платформы вы реализуете конкретный класс LEDS_IMPL:
class device_1_leds {
public :
  void actual_turn_on() { ... }
  ...
};
и используете его:
using dev_leds = leds<device_1_leds>;
...
dev_leds l;
turn_leds_on(l);
Никаких виртуальных методов, все, что компилятор сможет проверить в compile-time, все будет проверенно в compile-time. Ну и заинлайнено по максимуму.

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

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

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

Но я почему-то проверять не стал, будучи заочно увереным, что ассемблерный вывод будет длинее чем в текущей реализации.

С чего бы это?

eao197 ★★★★★
()

Наследование само по себе не несет оверхеда по производительности. Только виртуальные функции.

KivApple ★★★★★
()

clang вроде должен такое ругать, и вроде как есть флаг приравнивающий ругань на синтаксис к ошибкам

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

приравнивающий ругань на синтаксис к ошибкам

А без флага clang хавает произвольный синтаксис?

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

хз, я его не юзаю, по этому и не говорю, что инфа точная, а все через «вроде»

SR_team ★★★★★
()

Там выше уже описали: метод ITTest<Test>::test() по просту не инстанциируется, ибо его никто не вызывает. То если у тебя продакшен код, который будет вызывать нужный метод, то ошибка компиляции гарантирована — переживать нечего.

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

Более того, слово «интерфейс» для разных инстансов одного шаблонного класса имеет очень мало смысла. Интерфейсы (как по смыслу так и с точки зрения языка) у них разные. Так что действительно переживать нечего, и не всегда полиморфизм можно заменить шаблонами.

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

Потому что для того чтобы хороший человек стал использовать С++ на микроконтроллерах с ним должно произойти чтото нехорошее.

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

На сегодня воздержусь от технических аргументов.

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

Генерики компилируемьіе, а в равенскане таски из коробки

anonymous
()

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

чушь

next_time ★★★★★
()

кстати, где вы вообще в вашем коде разглядели наследование и виртуальные функции?

next_time ★★★★★
()

вы бы лучше задумались о том, что у вас sizeof(Test) == sizeof(class ITest) && sizeof(Test) != 0

и наследование с шаблонами тут вообще не причём

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

кстати, где вы вообще в вашем коде разглядели наследование и виртуальные функции?

Может начнем внимательно читать? Где я сазал, что оно там есть? Ну вот где?

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

вы бы лучше задумались о том, что у вас sizeof(Test) == sizeof(class ITest) && sizeof(Test) != 0

И?

и наследование с шаблонами тут вообще не причём

Еще раз, где я написал что у меня есть где-то наследование? Данный код - попытка реализовать интерфейс БЕЗ наследования. Зачем вы пишите, не вникунув в суть проблемы?

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

чушь

Смотря какие микроконтроллеры и какие требования. Микроконтроллер микроконтроллеру рознь. Сэкономить на памяти (а у нас вполне могут быть железки с 32кб памяти) и ассемблерном выхлопе - дело святое.

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

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

Зачем вы вообще отвечаете если не вникили в то, что именно я хочу. Я нигде ни слова не сказал о том, что у меня в ЭТОМ коде есть наследование. Перечитайте, пожалуйста.

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

Хм, а и правда работает. Чо-то я сразу не догадался так сделать. Спасибо.

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

вы вообще в курсе, что у наследования нулевая стоимость, как по объёму, так и по скорости?

а у виртуальных ф-ций стоимость вызова не больше, чем у указателя на функцию

Сэкономить на памяти (а у нас вполне могут быть железки с 32кб памяти) и ассемблерном выхлопе - дело святое.

32кб памяти — это типа уже мало? ппц вы там избаловались

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

И?

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

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

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

Мой код выдает выхлоп в одну(!) ассемблерную инструкцию. ОДНУ! Какие могут быть еще издержки меньше? Вы о чем? Что может иметь меньшие издержки чем этот код?

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

Формально издержки может и больше, но оптимизартор дает нормальный выхлоп. Там все инлайнится.

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

вы вообще в курсе, что у наследования нулевая стоимость, как по объёму, так и по скорости?

Может оно и так, но если там имеются виртуальные функции, то таки не нулевые?

32кб памяти — это типа уже мало? ппц вы там избаловались

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

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

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

Dudraug ★★★★★
() автор топика

Как заставить подобный код НЕ компилироваться?

Лол :-) Цепепе уже достал настолько, что «ваятели» придумывают как заставить компилятор не компилировать :-) Впрочем, в этом же вся суть программирования на цепепе, если присмотреться :-) Лол :-)

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

Может оно и так, но если там имеются виртуальные функции, то таки не нулевые?

виртуальные функции всё равно используются только там, где нужен либо switch, либо указатель на функцию

это раз

#define virtual void void

это два

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