Объединение в цепочку метода + наследование не играет хорошо вместе?

По http://php.net/manual/en/openssl.installation.php :

Чтобы использовать поддержку OpenSSL в PHP, вы также должны скомпилировать PHP --with-openssl[=DIR] .

blockquote>

Ubuntu включает этот флаг по умолчанию. Вместо того, чтобы пытаться выяснить, можете ли вы рассчитывать на его присутствие, используйте extension_loaded('openssl'), чтобы проверить его присутствие в вашем приложении, и изящно проваливайтесь с некоторыми инструкциями, если он отсутствует.

15
задан 27 February 2009 в 11:40
поделиться

15 ответов

Для второй проблемы, делая setAngle виртуальный должен добиться цели.

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

// Define a move helper function
template <typename T>
T& move(T& obj, Point& p){ return obj.move(p); };

// And the problematic line in your code would then look like this:
move(btn, Point(0,0)).setText("Hey");

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

8
ответ дан 1 December 2019 в 00:30
поделиться

Другие люди совершили нападки на вопросах проектирования. Можно отсортировать обходного решения, которое вводит проблема (хотя довольно грубым способом) использующий поддержку C++ ковариантного возврата.

struct Widget {
    virtual Widget& move(Point newPos) { pos = newPos; return *this; }
};

struct Label : Widget {
    Label& move(Point newPos) { pos = newPos; return *this; }
    Label& setText(string const& newText) { text = newText; return *this; }
    Label& setAngle(double newAngle) { angle = newAngle; return *this; }
};

struct Button : Label {
    Button& setAngle(double newAngle) {
        backgroundImage.setAngle(newAngle);
        Label::setAngle(newAngle);
        return *this;
    }
};

int main() {
    Button btn;

    // oops: Widget::setText doesn't exist
    btn.move(Point(0,0)).setText("Hey");

    // oops: calling Label::setAngle rather than Button::setAngle
    btn.setText("Boo").setAngle(.5); 
}

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

struct Widget {
    void move(Point newPos) { pos = newPos; }
};

struct Label : Widget {
    void setText(string const& newText) { text = newText; }
    void setAngle(double newAngle) { angle = newAngle; }
};

struct Button : Label {
    void setAngle(double newAngle) {
        backgroundImage.setAngle(newAngle);
        Label::setAngle(newAngle);
    }
};

int main() {
    Button btn;

    // oops: Widget::setText doesn't exist
    btn.move(Point(0,0));
    btn.setText("Hey");

    // oops: calling Label::setAngle rather than Button::setAngle
    btn.setText("Boo");
    btn.setAngle(.5); 
}
1
ответ дан 1 December 2019 в 00:30
поделиться

setText () и setAngle () действительно должен возвратить их собственные типы в каждом классе? Если Вы устанавливаете их всех для возврата Widget&, то можно просто использовать виртуальные функции следующим образом:

struct Widget {
    Widget& move(Point newPos) { pos = newPos; return *this; }
    virtual Widget& setText(string const& newText) = 0;
    virtual Widget& setAngle(double newAngle) = 0;
};

struct Label : Widget {
    virtual Widget& setText(string const& newText) { text = newText; return *this; }
    virtual Widget& setAngle(double newAngle) { angle = newAngle; return *this; }
};

struct Button : Label {
    virtual Widget& setAngle(double newAngle) {
        backgroundImage.setAngle(newAngle);
        Label::setAngle(newAngle);
        return *this;
    }
};

Обратите внимание, что, даже если тип возврата является Widget&, Кнопка - или функции уровня Маркировки все еще будет теми названными.

0
ответ дан 1 December 2019 в 00:30
поделиться

Ну, Вы знаете, что это - a Button таким образом, необходимо смочь бросить возвращенный Widget& как a Button& и продолжайте идти. Это действительно выглядит немного ужасным все же.

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

0
ответ дан 1 December 2019 в 00:30
поделиться

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

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

3
ответ дан 1 December 2019 в 00:30
поделиться

Я думаю (я не протестировал его), это сделает это шаблоны использования:

template<class T> struct TWidget {
    T& move(Point newPos) { pos = newPos; return (T&)*this; }
};

template<class T> struct TLabel : TWidget<T> { ... }

struct Label : TLabel<Label> { ... }

struct Button : TLabel<Button> { ... }

Примечания:

  • Любой/каждый базовый класс должен быть шаблоном, с отдельным нешаблонным листовым классом наверху (контрастируйте LabelT и Label классы).
  • Бросок мог быть a dynamic_cast если Вам нравится.
  • Вместо того, чтобы бросить"return *this", базовый класс мог содержать a T& как элемент данных (производный класс передал бы this конструктору базового класса), который был бы дополнительным элементом данных, но который избегает броска и я думаю, может разрешить состав вместо или а также наследование.
1
ответ дан 1 December 2019 в 00:30
поделиться

Это компилирует на gcc 4.3.2 и является видом смесительного шаблона.

#include <string>

using namespace std;

struct Point {
    Point() : x(0), y(0) {}
    Point(int x, int y) : x(x), y(y) {}

    int x, y;
};

template <typename T>
struct Widget {
    T& move(Point newPos) {
        pos = newPos;
        return *reinterpret_cast<T *> (this);
    }

    Point pos;
};

template <typename T>
struct Label : Widget<Label<T> > {
    T& setText(string const& newText) {
        text = newText;
        return *reinterpret_cast<T *> (this);
    }
    T& setAngle(double newAngle) {
        angle = newAngle;
        return *reinterpret_cast<T *> (this);
    }

    string text;
    double angle;
};

struct Button : Label<Button> {
    Button& setAngle(double newAngle) {
        backgroundImage.setAngle(newAngle);
        Label<Button>::setAngle(newAngle);
        return *this;
    }

    Label<Button> backgroundImage;
};

int main() {
    Button btn;

    // oops: Widget::setText doesn't exist
    btn.move(Point(0,0)).setText("Hey");

    // oops: calling Label::setAngle rather than Button::setAngle
    btn.setText("Boo").setAngle(0.0); 
}
1
ответ дан 1 December 2019 в 00:30
поделиться

Не с C++.

C++ не поддерживает различие, в ответ вводит, таким образом, нет никакого способа изменить статический тип ссылки, возвращенной из Widget.move (), чтобы быть более конкретным, чем Widget& даже при переопределении его.

C++ должен смочь проверить вещи во время компиляции, таким образом, Вы не можете использовать то, которое, что действительно возвращается из перемещения, кнопка.

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

Править: Да, я хорошо знаю о том, что в стандарте C++ говорится, что ковариантность возвращаемого значения законна. Однако в то время, когда я преподавал и практиковал C++, некоторые основные компиляторы (например, VC ++) не сделали. Следовательно, для мобильности мы рекомендовали против него. Возможно, что текущие компиляторы не имеют никакой проблемы с этим, наконец.

2
ответ дан 1 December 2019 в 00:30
поделиться

[напыщенная речь]

Да. Выйдите из этого метода, объединяющего бизнес в цепочку, и просто вызовите функции подряд.

Серьезно, Вы расплачиваетесь за разрешение этого синтаксиса, и я не извлекаю пользу, которую это предлагает.

[/напыщенная речь]

2
ответ дан 1 December 2019 в 00:30
поделиться

Можно расширить CRTP для обработки этого. решение monjardin идет в правильном направлении. Все, в чем Вы нуждаетесь теперь, является значением по умолчанию Label реализация для использования его в качестве листового класса.

#include <iostream>

template <typename Q, typename T>
struct Default {
    typedef Q type;
};

template <typename T>
struct Default<void, T> {
    typedef T type;
};

template <typename T>
void show(char const* action) {
    std::cout << typeid(T).name() << ": " << action << std::endl;
}

template <typename T>
struct Widget {
    typedef typename Default<T, Widget<void> >::type type;
    type& move() {
        show<type>("move");
        return static_cast<type&>(*this);
    }
};

template <typename T = void>
struct Label : Widget<Label<T> > {
    typedef typename Default<T, Widget<Label<T> > >::type type;
    type& set_text() {
        show<type>("set_text");
        return static_cast<type&>(*this);
    }
};

template <typename T = void>
struct Button : Label<Button<T> > {
    typedef typename Default<T, Label<Button<T> > >::type type;
    type& push() {
        show<type>("push");
        return static_cast<type&>(*this);
    }
};

int main() {
    Label<> lbl;
    Button<> btt;

    lbl.move().set_text();
    btt.move().set_text().push();
}

Тем не менее рассмотрите, стоит ли такое усилие маленькой добавленной премии синтаксиса. Рассмотрите альтернативные решения.

15
ответ дан 1 December 2019 в 00:30
поделиться

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

struct Widget {
    Widget& move(Point newPos) { pos = newPos; return *this; }
};

struct Label : Widget {
    Label& setText(string const& newText) { text = newText; return *this; }
    Label& setAngle(double newAngle) { angle = newAngle; return *this; }
    Label& move(Point newPos) { Widget::move(newPos); return *this; }
};

struct Button : Label {
    Button& setText(string const& newText) { Label::setText(newText); return *this; }
    Button& setAngle(double newAngle) {
        backgroundImage.setAngle(newAngle);
        Label::setAngle(newAngle);
        return *this;
    }
    Button& move(Point newPos) { Label::move(newPos); return *this; }
};

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

Но действительно, теперь, почему беспокойство с объединением в цепочку метода? Много раз эти функции будут вызваны менее тривиально и будут нужны в более длинных строках. Это действительно повредило бы удобочитаемость. Одно действие на строку - это - общее правило когда дело доходит до ++ и -- также.

4
ответ дан 1 December 2019 в 00:30
поделиться

Некоторое время там я думал, что могло бы быть возможно перегрузить немного необычное operator->() для объединения в цепочку методов вместо ., но это колебалось, потому что кажется, что компилятор требует идентификатора направо от -> принадлежать статическому типу выражения слева. Достаточно ярмарка.

Объединение в цепочку метода бедного человека

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

Вместо "рукописной формы":

btn.move(Point(0,0)); btn.setText("Hey");

Можно записать:

{Button& _=btn; _.move(Point(0,0)); _.setText("Hey");}

Нет, это не столь сжато как реальное объединение в цепочку с ., но это сохранит некоторый ввод, когда будет много параметров для установки, и это действительно обладает преимуществом, что это не требует никаких изменений кода в существующих классах. Поскольку Вы переносите всю группу вызовов метода в {} для ограничения объема ссылки можно всегда использовать тот же короткий идентификатор (например. _ или x) обозначать конкретное имя объекта, потенциально увеличивая удобочитаемость. Наконец, компилятор не испытает никаких затруднений при оптимизации далеко _.

2
ответ дан 1 December 2019 в 00:30
поделиться

A Button действительно a Label? Вы, кажется, нарушаете принцип замены Лисков. Возможно, необходимо полагать, что Шаблон "декоратор" добавляет поведения к Виджетам.

Если Вы настаиваете на структуре, как, можно решить проблему как так:

struct Widget {
    Widget& move(Point newPos) { pos = newPos; return *this; }
    virtual ~Widget();  // defined out-of-line to guarantee vtable
};

struct Label : Widget {
    Label& setText(string const& newText) { text = newText; return *this; }
    virtual Label& setAngle(double newAngle) { angle = newAngle; return *this; }
};

struct Button : Label {
    virtual Label& setAngle(double newAngle) {
        backgroundImage.setAngle(newAngle);
        Label::setAngle(newAngle);
        return *this;
    }
};

int main() {
    Button btn;

    // Make calls in order from most-specific to least-specific classes
    btn.setText("Hey").move(Point(0,0));

    // If you want polymorphic behavior, use virtual functions.
    // Anything that is allowed to be overridden in subclasses should
    // be virtual.
    btn.setText("Boo").setAngle(.5); 
}
4
ответ дан 1 December 2019 в 00:30
поделиться

C++ действительно поддерживает ковариантность возвращаемого значения на виртуальных методах. Таким образом, Вы могли получить что-то как то, что Вы хотите с небольшой работой:

#include <string>
using std::string;

// member data omitted for brevity

// assume that "setAngle" needs to be implemented separately
// in Label and Image, and that Button does need to inherit
// Label, rather than, say, contain one (etc)


struct Point
{
    Point() : x(0), y(0) {};
    Point( int x1, int y1) : x( x1), y( y1) {};

    int x;
    int y;
};

struct Widget {
    virtual Widget& move(Point newPos) { pos = newPos; return *this; }
    virtual ~Widget() {};

    Point pos;
};

struct Label : Widget {
    virtual ~Label() {};
    virtual Label& move( Point newPos) { Widget::move( newPos); return *this; }

    // made settext() virtual, as it seems like something 
    // you might want to be able to override
    // even though you aren't just yet
    virtual Label& setText(string const& newText) { text = newText; return *this; }
    virtual Label& setAngle(double newAngle) { angle = newAngle; return *this; }

    string text;
    double angle;
};

struct Button : Label {
    virtual ~Button() {};
    virtual Button& move( Point newPos) { Label::move( newPos); return *this; }
    virtual Button& setAngle(double newAngle) {
        //backgroundImage.setAngle(newAngle);
        Label::setAngle(newAngle);
        return *this;
    }
};

int main()
{
    Button btn;

    // this works now
    btn.move(Point(0,0)).setText("Hey");

    // this works now, too
    btn.setText("Boo").setAngle(.5); 

  return 0;
}

Обратите внимание, что необходимо использовать виртуальные методы для того, чтобы сделать что-то вроде этого. Если они не будут виртуальными методами, то 'повторно реализованные' методы приведут к сокрытию имени, и названный метод будет зависеть от статического типа переменной, указателя или ссылки, таким образом, это не могло бы быть, он исправляет метод, если указатель базы или ссылка используются.

3
ответ дан 1 December 2019 в 00:30
поделиться

Really, method chaining is a bad idea due to poor exception safety. Do you really need it?

1
ответ дан 1 December 2019 в 00:30
поделиться
Другие вопросы по тегам:

Похожие вопросы: