LINUX.ORG.RU

Статическая проверка статического интерфейса в статическом c++

 


0

4

Как статически проверить что у наследника есть нужная функция?

Пример:

template <typename Heir>
class SToString {
public:
	SToString () {
		// СТАТИЧЕСКАЯ ПРОВЕРКА
		std::string (Heir::* tmp)() = &Heir::toStringImpl;
	}

	std::string toString() {
		return static_cast<Heir*>(this)->toStringImpl();
	}
};

class Foo : public SToString<Foo> {
public:
	std::string toStringImpl() {
		return "Foo";
	}
};

Вот это вот работает, но только если в коде присутствует вызов конструктора Foo. И еще это костыль, который сыпет ворнинги.

Есть ли нормальное решение, например со static_assert-ом.

★★★

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

Как статически проверить что у наследника есть нужная функция?

Тебе нужен виртуальный метод. Если нужно гарантировать его наличие у наследника - абстрактный виртуальный метод.

std::string (Heir::* tmp)() = 

Лишнее.

anonymous
()

Как-то так?

#include <type_traits>

template <typename Heir>
class SToString {
public:
    std::string toString() {
        static_assert(std::is_member_function_pointer<decltype(&Heir::toStringImpl)>::value, "class derived from SToString should implement toStringImpl()");
        return static_cast<Heir*>(this)->toStringImpl();
    }
};

class Foo : public SToString<Foo> {
public:
    std::string toStringImpl() {
        return "Foo";
    }
};

В отсутствие реализации toStringImpl будет срабатывать кшна не assert, а ошибка no member named 'toStringImpl', но ошибка укажет на строку с assert где будет виден текст сообщения. Лишних варнингов также нет.

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

Странно, но не работает.

#include <iostream>
#include <array>
#include <type_traits>

template <typename T>
constexpr bool has_to_string(decltype(&T::toStringImpl)) {
	return true;
}

template <typename T>
constexpr bool has_to_string(...) {
	return false;
}

template <typename Heir>
class SToString {
public:
	SToString () {
		std::string (Heir::* tmp)() = &Heir::toStringImpl;
	}

	std::string toString() {
		return static_cast<Heir*>(this)->toStringImpl();
	}
	
	static_assert(has_to_string<Heir>(0), ""); // всегда срабатывает, даже когда функция есть
};


class Foo : public SToString<Foo> {
public:
	std::string toStringImpl() {
		return "Foo";
	}
	static void foo() {}
};

static_assert(has_to_string<Foo>(0), ""); // работает как надо
Kuzy ★★★
() автор топика
Ответ на: комментарий от Kuzy

Что значит достаточно? У моего варианта требований меньше - не нужен ничего пихать в конструктор, в то же время static_cast защищён статической проверкой, но только если он реально используется.

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

Что значит достаточно?

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

И static_assert там не нужен. Можно его оставить, только ради аннотации: http://ideone.com/TkwuPi

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

Што? Ты либо используешь (в тех же тестах) toString, тогда у тебя есть статическая проверка. Либо не используешь никак - тогда тебе и проверка не нужна.

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

Статическая проверка статического интерфейса в статическом c++ (комментарий)

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

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

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

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

Kuzy ★★★
() автор топика
Ответ на: комментарий от Kuzy
#include <type_traits>
template <typename T> struct A { A() { static_assert(std::is_member_function_pointer<decltype(&T::g)>::value, "..."); } };
struct B : A<B> { int g () { return 42; } };
template struct A<B>;

man explicit instantiation.

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

Точно, полущ для костыляторства на препроцессоре.

Хотя, почти ничем не отличается от вызова конструктора (но ворнингов таки нет). static_assert там не нужен, без него тоже работает.

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

Ещё extern template struct A<B>, чтобы в бинарник лишнего не писал.

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

А вот тут уже и макросы не помогут.

template <typename T>
class Foo : public SToString<Foo> {
public:
	std::string toStringImpl() {
		return "Foo";
	}
};

template class SToString<Foo< ??? >>; // ???
Kuzy ★★★
() автор топика
Ответ на: комментарий от Kuzy

???

Любой T удовлетворяющий соответствующему концепту (void в простейшем случае). И тогда уже без extern. Ну и class Foo : public SToString<Foo<T>>. Но это при условии, что все возможные специализации одинаково содержат метод или нет, иначе можно проверить SToString<Foo<A>> при инстанциации, но иметь специализацию SToString<Foo<B>> без метода, так что ошибка (компиляции) возникнет только при инстанцировании SToString<Foo<B>> (если его тоже явно не инстанцировать вызвав тем самым проверку).

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

Угу, оставил костыль из ОП-поста и добавляю инстанциации по вкусу.

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

Использовать C++
Выбирать простые, но не оптимальные решения.

man преждевременная оптимизация

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