Delphi vs C++ vs C#
, ну и Java немножко, где-то ближе к старшему байту номеров страниц
![]() |
Наши проекты:
Журнал · Discuz!ML · Wiki · DRKB · Помощь проекту |
|
| ПРАВИЛА | FAQ | Помощь | Поиск | Участники | Календарь | Избранное | RSS |
| [216.73.216.156] |
|
|
Правила раздела:
| Страницы: (495) « Первая ... 312 313 [314] 315 316 ... 494 495 ( Перейти к последнему сообщению ) |
Delphi vs C++ vs C#
, ну и Java немножко, где-то ближе к старшему байту номеров страниц
|
Сообщ.
#4696
,
|
|
|
|
Цитата D_KEY @ Я имел не встроенное в язык средство, а, скажем так, возможность реализации пользователями языка. Ну, я и приравниваю возможность безгемморной реализации к встроенным возможностям. Второе может быть предпочтительнее, поскольку не утяжеляет язык. По-моему, korvin показывал мемоизацию функции в лиспе так, что клиенты об этом и не догадываются - ежу понятно, что одной этой возможности хватит чтобы чуть ли не полностью реализовать АОП. Но ты же понимаешь, что такая же фича в джавошарпе, не говоря уже о плюсах, равносильна бескомпромиссному горячему патчингу кода. Ну, допустим, в виртуальной машине это ещё как-то пытаются сделать, но в плюсах... здесь только в языке. Но так и языки разные, для разных вещей - ты же прекрасно понимаешь. Чёрт, где же я видел статью про горячий патч сишного кода... пошёл искать! |
|
Сообщ.
#4697
,
|
|
|
|
Цитата Qraizer @ D_KEY, никто не будет в один язык запихивать вообще всё, что напридумано в области дискретной математики за 60 лет. А об этом речи и не шло Прошу прощения, может, плохо формулирую. |
|
Сообщ.
#4698
,
|
|
|
|
Нормальные, со стоимостью вызова примерно равной виртуальным методам, с учётом любой иерархии наследования - неа, без костылей никак... И это я ещё требование не править имеющийся код не добавил...Вот мне не далее как месяц назад просто оооочень нужны были... в условиях цейтнота пришлось констрячить трёхколёсный велик на шабломакросах... очень уродский, надо сказать, велик. |
|
Сообщ.
#4699
,
|
|
|
|
Ты забыл упомянуть "в делфи"! Цитата Romkin @ Это подразумевает именно подключаемую в процессе сборки объекта конкретную реализацию ![]() Как один из множества случаев. Цитата Qraizer @ Ежели так, то и вопросов нет. Но вроде бы у них со scorpion о чём-то магическом разговор шёл. Или я не понял. Разговор шел о том, как юзнуть неизвестный на этапе компиляции интерфейс, который ты запросил у неизвестного на этапе компиляции объекта! Но потом выяснилось, что под неизвестным на этапе компиляции интерфейсом, подразумевался как раз таки известный на этапе компиляции интерфейс. Просто в понимании DesweR'а видимо под неизвестным на этапе компиляции интерфейсом, понималось именно(если исходить из твоего кода): ![]() ![]() I& queryInterface(T& src) Буква "I" в этом всем выражении, понимаешь? А я сразу не понял его фразу: "запросить интерфейс, неизвестный на этапе компиляции, у объекта, неизвестного на этапе компиляции", поэтому и возникло у меня куча вопросов. Добавлено Вопрос не в тему У меня возник вопрос. Вопрос конечно, не в тему, по крайней мере не вписывается в рамки этой темы. Но я знаю что тут обитают шарписты/С++сники, которые могут помочь. Дело вот в чем. Есть Window Forms компонент, реализован он в виде dll, из себя представляет диалог, т.е. образно диалог, на нем RichEdit, со всякими плюшками, таб шит, кнопки OK, Cancelи т.д. . Мне все это чудо требуеца встроить в MFC фрейм, конкретно, в класс который наследуется от CView. Вопрос к тем кто с таким сталкивался, я могу оформить этот диалог в свою вьюшку? Как встроить Control Library написаное на .NET в вьюшку - я знаю. Реализовать в виде ActiveX, хз, думал об этом, но пока не пытался, диалог в мастер визарде, сцуко, выглядит - как диалог, со всеми кнопачками(ОК, Отмена, Закрыть, Свернуть). Вообще как быть в таком случае? Меня не интерсуют детали реализации, мне хотя бы дать толчок куда копать? ActiveX ? Встрить WinForms в Control Library и уж то что получилось в вьюшку? Или вообще можно реализовать так чтобы .NET диалог встроился в MFC::CView ? То, что там наследоваться нужно от WindowFormsView - это я знаю, на примере Control Library разобрался... Просто еще(даже незнаю проблема или нет?) диалог этот и все его компоненты - это по сути стороние компоненты DevExpress... |
|
Сообщ.
#4700
,
|
|
|
|
Нормальные, с линейной, зависимой от количества классов в иерархии, стоимостью вызова, независящей от количества перекрытий мультиметода - релизованы Flex Ferrum-ом. Ну или где-то им подсмотренные, но это вряд ли. Там были variadic templates, сравнительно недавнее приобретение. Я её на всякий случай перенёс на C++03, заменив variadic templates на списки типов. Ну, правда там ещё есть зависимость от количества параметров мультиметода, тоже линейная, но она константная величина для фиксированной сигнатуры. Получается что-то вроде O(Σmn), где mn - глубина иерархии n-го параметра мультиметода. Это неоптимизированный код. Насколько сможет соптимизировать компилятор, судить сложно. Кроме этой реализации я видел только у Александреску, и они мне нравятся меньше. Об остальных судить не берусь.
|
|
Сообщ.
#4701
,
|
|
|
|
Qraizer, не, такая сложность меня не устраивает
|
|
Сообщ.
#4702
,
|
|
|
|
Счас вот проверил. 4 класса в иерархии, 2 параметра. Intel C++ compiler сделал 4 косвенных вызова, по два для каждого параметра: один - на восстановление динамического типа очередного параметера, второй - на переход к следующему параметру. Можно предположить, что по два и останется, независимо (в разумных пределах, зависящих от запаса глубины мыслей оптимизатора) от количества наследников. MSVC 2008 сделал в общем-то то же, только отдетектил два хвостовых вызова и заменил на безусловные переходы.
|
|
Сообщ.
#4703
,
|
|
|
|
Qraizer, а реализацию посмотреть можно?
|
|
Сообщ.
#4704
,
|
|
|
|
Меньше, чем по вызову на параметр, боюсь, не получится даже в поддержкой на уровне языка. По-любому по VMT придётся восстанавливать тип каждого параметра отдельно, и где-то хранить n-размерную матрицу указателей на перекрытые методы, а это скорее всего ещё один вызов. Так что на этом примере получилось бы три вызова.
Добавлено Вот исходная от Flex Ferrum-а. Я её там помимо переноса под C++03 причёсывать начал, кое-где поправил, кое-что убрал, комментов понаставил, но не закончил, времени не нашёл. Добавлено Пока выглядит вот так: ![]() ![]() #include <iostream> #include <iomanip> /*******************************************************\ ** Списки типов, заменяющие variadic templates в C++0x ** \*******************************************************/ /* Терминальный тип */ struct NullType {}; /* Список типов. L - голова, любой тип, кроме терминального и списка. R - хвост, либо терминальный тип, либо список */ template <typename L, typename R> struct TList { typedef L Head; typedef R Tail; }; /* Генератор "плоской" иерархии на основе списка типов T. Все типы из списка T применяются к шаблону Node. Первый его параметр - параметризированный тип из списка, второй - тип возвращаемого значения мультиметода. Все получившиеся Node<T::Head> не состоят в родстве относительно друг друга, что обеспечивает их равноправность при разрешении перегрузки. От базы (самого производного класса GenHierarchy<>) можно однозначно добраться до любого узла путём простого кастования базы к нему при условии уникальности каждого типа в списке. В нашем случае неуникальность не нужна. В иных применениях поддержка таких случаев может быть несложно добавлена. */ template <template <typename, typename> class Node, typename Ret, typename T> struct GenHierarchy; template <template <typename, typename> class Node, typename Ret, typename L, typename R> struct GenHierarchy<Node, Ret, TList<L, R> >: public Node<L, Ret>, public GenHierarchy<Node, Ret, R> { }; template <template <typename, typename> class Node, typename Ret> struct GenHierarchy<Node, Ret, NullType> { }; /* Метафункция получения последнего элемента списка */ template <typename T> struct Last; template <typename L, typename R> struct Last<TList<L, R > > { typedef typename Last<R>::result result; }; template <typename T> struct Last<TList<T, NullType> > { typedef T result; }; /*******************************************************\ ** Наши тестовые классы и их список ** \*******************************************************/ class Base; class D1; class D2; class D3; /* Самый базовый класс должен быть последним. Впрочем, заглянув к Александреску на книжку чаю, поиск самого базового из предоставленных можно автоматизировать. Я не стал копить сюда лишнего метакода. */ //typedef TList<D3, TList<D2, TList<D1, TList<Base, NullType> > > > Hierarchy; typedef TList<D2, TList<D1, TList<D3, TList<Base, NullType> > > > Hierarchy; /********************************************************************************\ ***************** Далее следует реализация мультиметодов **************** \********************************************************************************/ /* Узел "плоской" иерархии */ template <typename T, typename Ret> class AcceptorBase { public: virtual Ret Accept(T* obj) = 0; }; /* Генератор узлов "плоской" иерархии. Первый параметр - исходный список, второй - тип возвращаемого значения мультиметода */ template <typename TL, typename Ret> class Acceptor: public GenHierarchy<AcceptorBase, Ret, TL> { public: typedef TL TypeList; /* Создаёт метафункцию type, конкретизирущую в построенной иерархии метафункцию двух параметров-типов GA типом B */ template <typename B, template <typename, typename> class GA> struct MakeAcceptor { typedef GA<B, TL> type; }; }; /* Создаём "плоскую" иерархию по нашему списку */ typedef Acceptor<Hierarchy, void> ConcreteAcceptor; /* Определяем несколько мультиметодов */ void TestDispatch(Base* b1, Base* b2) { std::cout << "Double Base-Base Dispatch" << std::endl; } void TestDispatch(D1* o1, Base* o2) { std::cout << "Double D1-Base Dispatch" << std::endl; } void TestDispatch(D1* o1, D2* o2) { std::cout << "Double D1-D2 Dispatch" << std::endl; } void TestDispatch(D2* o1, D3* o2) { std::cout << "Double D2-D3 Dispatch" << std::endl; } void TestDispatch(D3* o1, D2* o2) { std::cout << "Double D3-D2 Dispatch" << std::endl; } void TestDispatch(D3* o1, D3* o2) { std::cout << "Double D3-D3 Dispatch" << std::endl; } /********************************************************************************\ ***************** Обобщённый диспетчер **************** \********************************************************************************/ /* Параметр - конкретный диспетчер. Должен: предоставлять тип ret_type являющийся типом возвращаемого значения мультиметода; иметь перегруженный operator(), принимающий любую по меньшей мере определеяемую набором имеющихся мультиметодов комбинацию параметров из списка типов и возвращающий ret_type; уметь конструироваться bool-ом (в общем-то требование избыточное, сейчас используется в исследовательских целях) и использовать его для реверса порядка параметров; в ответ на вызов его operator() вызывать собственно мультиметоды с тем же набором паметров. */ template <typename DI> struct Dispatcher { typedef typename DI::ret_type ret_type; // Тип возвращаемого значения к себе /* Акцепторы левого и правого параметров. Сейчас совпадают, ибо предполагается, что параметры мультиметода симметричны. В целом же это необязательно */ template <typename B, typename TL> struct LeftAcceptorCaller; template <typename B, typename TL> struct RightAcceptorCaller; template <typename L> struct AcceptorMaker { typedef typename ConcreteAcceptor::MakeAcceptor<ret_type, LeftAcceptorCaller >::type left_type; typedef typename ConcreteAcceptor::MakeAcceptor<L, RightAcceptorCaller>::type right_type; }; template <typename L, typename T, typename R> struct RightAcceptorCaller<L, TList<T, R> > : public RightAcceptorCaller<L, R> { typedef RightAcceptorCaller<L, TList<T, R> > this_type; typedef RightAcceptorCaller<L, R > base_type; RightAcceptorCaller(L* l, DI* di) : base_type(l, di) {} ret_type Accept(T* obj) { return base_type::m_DispImpl->operator()(base_type::m_Left, obj); } }; template <typename L, typename R> struct RightAcceptorCaller<L, TList<R, NullType> > : public ConcreteAcceptor { typedef RightAcceptorCaller<L, TList<R, NullType> > this_type; RightAcceptorCaller(L* l, DI* di) : m_Left(l), m_DispImpl(di) {} ret_type Accept(R* obj) { return m_DispImpl->operator()(m_Left, obj); } L *m_Left; DI *m_DispImpl; }; template <typename T> struct LeftAcceptorCaller<ret_type, TList<T, NullType> > : public ConcreteAcceptor { typedef LeftAcceptorCaller<ret_type, TList<T, NullType> > this_type; LeftAcceptorCaller(T* r, DI* di) : m_Right(r), m_DispImpl(di) {} ret_type Accept(T* obj) { typedef typename AcceptorMaker<T>::right_type acceptor; acceptor a(obj, m_DispImpl); return m_Right->Accept(a); } T *m_Right; DI *m_DispImpl; }; template <typename L, typename R> struct LeftAcceptorCaller<ret_type, TList<L, R> > : public LeftAcceptorCaller<ret_type, R> { typedef LeftAcceptorCaller<ret_type, TList<L, R> > this_type; typedef LeftAcceptorCaller<ret_type, R > base_type; typedef typename Last<R>::result most_base_type; LeftAcceptorCaller(most_base_type* r, DI* di) : base_type(r, di) {} ret_type Accept(L* obj) { typedef typename AcceptorMaker<L>::right_type acceptor; acceptor a(obj, base_type::m_DispImpl); return base_type::m_Right->Accept(a); } }; ret_type operator ()(typename AcceptorMaker<ret_type>::left_type::this_type::most_base_type* l, typename AcceptorMaker<ret_type>::left_type::this_type::most_base_type* r) { typedef typename AcceptorMaker<ret_type>::left_type acceptor; DI di; acceptor a(r, &di); return l->Accept(a); } }; template <typename T = void> class TestDispatcher { public: typedef T ret_type; template <typename L, typename R> T operator()(L* l, R* r) { return TestDispatch(l, r); } template <typename L, typename R> T TestDispatch(L* l, R* r) { return ::TestDispatch(l, r); } }; /* Макро для удобства */ #define MAKE_ACCEPTABLE(A, C) virtual void Accept(A& a) \ { \ return static_cast<AcceptorBase<C, void>*>(&a)->Accept(this); \ } class Base { public: MAKE_ACCEPTABLE(ConcreteAcceptor, Base); }; class D1 : public Base { public: MAKE_ACCEPTABLE(ConcreteAcceptor, D1); }; class D2 : public D1 { public: MAKE_ACCEPTABLE(ConcreteAcceptor, D2); }; class D3 : public Base { public: MAKE_ACCEPTABLE(ConcreteAcceptor, D3); }; int main() { Base b; D1 d1; D2 d2; D3 d3; std::cout << std::boolalpha; { Dispatcher<TestDispatcher<> > d; d(&b , &b ); d(&b , &d1); d(&b , &d2); d(&b , &d3); std::cout << std::endl; d(&d1, &b ); d(&d1, &d1); d(&d1, &d2); d(&d1, &d3); std::cout << std::endl; d(&d2, &b ); d(&d2, &d1); d(&d2, &d2); d(&d2, &d3); std::cout << std::endl; d(&d3, &b ); d(&d3, &d1); d(&d3, &d2); d(&d3, &d3); } std::cout << std::endl; return 0; } Добавлено Вообще, хотелось в ним поболтать на кое-какие темы и родить нормальную библиотечную версию. Он даже не был против, но рутина затянула. Программа максимум - параметризировать количество параметров мультиметода. |
|
Сообщ.
#4705
,
|
|
|
|
Qraizer, благодарю.
Цитата Qraizer @ Меньше, чем по вызову на параметр, боюсь, не получится даже в поддержкой на уровне языка. Ну, вот у мужиков ведь получилось ![]() Цитата In particular, the runtime cost of calling an open multimethod is constant and less than the cost of a double dispatch (two virtual function calls). Finally, we provide a sketch of a design for open multi-methods in the presence of dynamic loading and linking of libraries. |
|
Сообщ.
#4706
,
|
|
|
|
Напомните плз, про мультиметоды, только в вики не посылайте плз, а то там непонятно както
|
|
Сообщ.
#4707
,
|
|
|
|
scorpion, как я понял, он неконкретно выразился. Под неизвестным заранее он подразумевал неизвестным не на момент компиляции, а на момент написания. С шаблонами у нас та же картина - когда мы пишем шаблонный код, мы не знаем параметров шаблона, но они известны в точках использования, т.е. на момент компиляции аргументам обязательно что-то сопоставлено. Лучше пусть сам уточнит.
D_KEY, ну... сорри тогда. Я воспринял так, что ты предъявляешь это всё как большой недостаток. |
|
Сообщ.
#4708
,
|
|
|
|
Цитата Qraizer @ Под неизвестным заранее он подразумевал неизвестным не на момент компиляции, а на момент написания. Да, это я понял уже. Я уже и намекал ему на шаблоны, в том и дело, что запутал он меня просто. |
|
Сообщ.
#4709
,
|
|
|
|
Цитата scorpion @ Цитата DesweR @ Всё правильно, реализуются явно указанные интерфейсы, такова идеология COM. Пол года назад уже был разговор: Ну и там же явно сказано: Цитата DesweR @ Тоже самое касается и наследования интерфейсов, должен соблюдаться не только сигнатурный контракт, но и гарантированно семантический, а для этого класс A, должен содержать реализацию интерфейсов и child и parent. Так понятней? Цитата DesweR @ Тоже самое касается и наследования интерфейсов, должен соблюдаться не только сигнатурный контракт, но и гарантированно семантический, а для этого класс TBC1, должен содержать реализацию интерфейсов и IB, IC и IA. ![]() ![]() IA = Interface procedure Foo; end; IB = Interface(IA) //***// end; IC = Interface(IA) //***// end; TBC1 = class(TInterfacedObject, IB, IC) private procedure Foo; end; т.е. ![]() ![]() TBC1 = class(TInterfacedObject, IA, IB, IC) private procedure Foo; end; Цитата MyNameIsIgor @ Цитата DesweR @ В таком случае все варианты, когда реализация должна быть скрыта в классе, идут лесом. Оно вообще тогда надо? Не понял... Я имею ввиду случаи, когда реализации интерфейсов скрываются либо в целях ограничения доступа извне, либо из-за удобства декомпозиции самого функционала. ![]() ![]() IGuest = interface procedure ReadDocument; end; IUser = interface(IGuest) procedure WriteDocument; end; IAdmin = interface(IUser) procedure CreateDocument; procedure DeleteDocument; end; TMailService = class(TInterfacedObject, IGuest, IUser, IAdmin) private procedure ReadDocument; procedure WriteDocument; procedure CreateDocument; procedure DeleteDocument; end; ILogger = interface procedure SendMessage; end; ILog = interface procedure ReadMessages; end; IConfig = interface procedure FormatFile; procedure OutputDirectory; procedure MessageFilter; end; TLogService = class(TInterfacedObject, ILogger, ILog, IConfig) private procedure SendMessage; procedure ReadMessages; procedure FormatFile; procedure OutputDirectory; procedure MessageFilter; end; Но. И в любых других случаях, также нет смысла делать реализацию публичной, без какой-либо необходимости на то, т.к. клиенты всё равно работают через интерфейсы, а не через реализующие их объекты. Цитата Qraizer @ scorpion, как я понял, он неконкретно выразился. Под неизвестным заранее он подразумевал неизвестным не на момент компиляции, а на момент написания. Параметрический метод ввёл вас слегка в заблуждение. ![]() ![]() procedure Reg<T>; так ![]() ![]() procedure Reg(); Нет, я действительно имел ввиду неизвестный на момент компиляции. Либо пользователем из gui, либо каким-нибудь алгоритмом, по заданным условиям, определяются доступные реализации и регистрируются в фабрике. ![]() ![]() for i := 0 to List.Count - 1 do Factory.Reg(List[i]); Цитата Qraizer @ DesweR, это подправление здорово изменило отношения классов. Это ли не редизайн? Интересно будет послушать твоего коллегу, который после такой небольшой подправки получит у себя поведение, описываемое Flex Ferrum-ом. Эм... но ты сам это просил. Хорошо, а что тебя будет ждать, если это повторить на абстрактных классах? |
|
Сообщ.
#4710
,
|
|
|
|