LINUX.ORG.RU

Опять хочется странного

 


0

4

Хочу вот так:

class IGraph {
public:
    virtual void foo(/*some magic to support variable amount of args in implementations*/) = 0;
};
 
class FirstGraph : public IGraph {
public:
    void foo(int id) {/*some code*/}
};
 
class SecondGraph : public IGraph {
public:
    void foo(int id, unsigned int weight) {/*some code*/}
};
Как можно реализовать такую магию в C++11?


ИМХО можно сделать virtual void foo (/*variable amount of args*/) = 0, а в потомках делать assert'ы на количество и типы аргументов.

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

assert'ы на количество и типы аргументов

Что-то это как-то некрасиво. Должно же быть нечто более элегантное...

maked0n
() автор топика

Именно вот так никак.

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

Вряд ли кто-то понял

У меня есть интерфейс, в нем метод. В зависимости от реализации количество и типы аргументов этого метода могут быть различными. Это как-то обобщается шаблонами/еще чем-то, чего я не знаю?

maked0n
() автор топика

Не надо так делать. Вообще.

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

Что-то это как-то некрасиво. Должно же быть нечто более элегантное...

Не думаю.
Кстати, с assert'ами на типы могут быть вопросы.
Вот еще вариант, в конце статьи: http://nerdparadise.com/forum/openmic/5712/

Kroz ★★★★★
()

Еще вариант

Есть еще мысль. Ты ж можешь в FirstGraph, SecondGraph делать наследуемую foo private, а вместо нее объявить функции c другим количеством аргументов.

Если будет ругаться, я бы сделал через промежуточный класс, чтобы не было конфликтов. Или, еще вариант из той же серии - изначально сделать foo-variardic private и объявить друзей.

Пробуй.

Kroz ★★★★★
()

Погоди! Если у тебя прям вот так, то всё совсем просто:

class IGraph {
public:
    virtual void foo(int id) { throw logic_error("Error in source code"); }
    virtual void foo(int id, unsigned int weight) { throw logic_error("Error in source code"); }
};
 
class FirstGraph : public IGraph {
public:
    void foo(int id) {/*some code*/}
};
 
class SecondGraph : public IGraph {
public:
    void foo(int id, unsigned int weight) {/*some code*/}
};

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

У меня есть интерфейс, в нем метод. В зависимости от реализации количество и типы аргументов этого метода могут быть различными. Это как-то обобщается шаблонами/еще чем-то, чего я не знаю?

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

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

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

У тебя нет интерфейса, тебе показалось. Интерфейс абстрагирует реализации в обобщённое API, а не делает наоборот. Может быть тебе нужно иметь метод void foo(int, unsigned) и воторой аргумент в реализациях обрабатывать ~ по желанию.

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

Если у тебя прям вот так

Это явно не всё, что тебе надо

Нет, все же не совсем. Вся проблема в том, что реализации интерфейса IGraph буду писать не я и я понятия не имею, сколько аргументов захотят добавить в ф-цию foo(), помимо первого - id.

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

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

Нужно все дополнительные аргументы заворачивать в структуру которую и будут дальше расширать. Либо передавать вместо неё другой интерфейс из геттеров, типа void foo(int, Opts&) или void foo(int, IOpts&)

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

А тогда зачем тебе вообще объявлять foo в родительском классе?

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

Вся фишка ООП в том, что некая функция будет получать на вход IGraph и сможет вызвать foo, и это будет абсолютно корректно отработано вне зависимости от реального типа объекта (главное чтобы это был наследник IGraph). А в твоём случае код, работающий с экземляром IGraph так сделать не может и ему необходимо сначала как-то понять, какой же на самом деле класс ему дали (с помощью RTTI или дополнительного виртуального метода, который возвращает разные значения в разных классах наследниках). А если класс известен, то что мешает сделать static_cast к нужному типу и уже вызвать foo с нужными параметрами? При этом виртуальность нафиг не нужна.

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

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

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

Или можешь заменить std::map<std::string, std::string> на std::unordered_map<int, std::experimental::any>

Вместо std::experimental::any можешь заюзать любую другую имплементацию «variant», или даже просто void* (что не C++-стиль).

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

Нужно все дополнительные аргументы заворачивать в структуру

Спасибо.

maked0n
() автор топика

virtual void foo(IParams&)

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

invy ★★★★★
()

Опять хочется странного

Тогда можете почитать: Дж. Толкиен «Хоббит или туда и обратно», Ричард Бах «Чайка по имени Джонатан Ливингстон», рассказы Александра Грина («Алые паруса», «100 верст по реке», «Корабли в Лиссе», «Комендант порта», «Бегущая по волнам»), Джером К. Джером «Трое в лодке, не считая собаки,», «Трое на 4-х колесах», Антуан де Сент Экзюпери «Маленький принц», Льюис Кэрролл «Алиса в стране чудес», «Алиса в Зазеркалье».

anonymous
()

Получается у FirstGraph::foo один набор аргументов, у SecondGraph::foo другой и вызывающая функция должна знать foo какого класса она вызывает. Зачем при таком раскладе виртуальность?

anonymous
()

Может тогда так?

class IGraph {
public:
    virtual void foo() = 0;
};
 
class FirstGraph : public IGraph {
private:
    int id;
public:
    FirstGraph(int id): id(id) {}
    void foo() {/*some code*/}
};
 
class SecondGraph : public IGraph {
private:
    int id;
    unsigned int weight;
public:
    SecondGraph(int id, unsigned int weight): id(id), weight(weight) {}
    void foo() {/*some code*/}
};
vega
()
template<typename ... args>
class IGraph {
public:
    virtual void foo(args...) = 0;
};

class FirstGraph : public IGraph<int> {
public:
    void foo(int id)override {/*some code*/}
};

class SecondGraph : public IGraph<int,unsigned int> {
public:
    void foo(int id, unsigned int weight)override {/*some code*/}
};

?

anonymous
()
Ответ на: комментарий от Begemoth
#include <iostream>

using namespace std;

template<typename ...args>
class IGraph {
public:
    virtual void foo(args...) = 0;
};

class FirstGraph : public IGraph<int> {
public:
    virtual void foo(int a)override
    {
        cout<<"FirstGraph::foo(int) with args: "<<a<<endl;
    }
};

class SecondGraph : public IGraph<int,unsigned int> {
public:
    virtual void foo(int a ,unsigned int b) override
    {
        cout<<"SecondGraph::foo(int,unsigned in) with args: "<<a<<" "<<b<<endl;
    }
};
template<typename ... _args>
constexpr inline void GraphFooCall(IGraph<_args...>* target,_args... args)
{
    IGraph<_args...>* ig = target;
    ig->foo(args...);
};

int main()
{
    FirstGraph* g1 = new FirstGraph;
    SecondGraph* g2 = new SecondGraph;
    GraphFooCall(g1,1);
    GraphFooCall(g2,1,(unsigned int)2);
    return 0;
}

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

Ну хотели же странного, нате

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