Здравствуйте, AndrewVK, Вы писали:
AVK>Здравствуйте, <Аноним>, Вы писали:
А>>Так что для сохранения настроек такая технология подходит очень хорошо, а для сохранения реальных данных (по крайней мере в моем случае) как то не катит
AVK>А для сохранения реальных данных предназначена не сериализация, а СУБД.
А как передавать такие объемы данных клиенту? В этой ветке где-то был разговор про SOAP, так что вот конкретный пример. А передать по низкоскоростному каналу связи лишние несколько метров как-то не хочется.
По моему в RSDN как раз была статься по ручной сереализации dataset для решения этих проблем.
PS Да, и на моей практике, если менялись типы данных или запросов, то все равно менялась логика, поэтому прелесть определения типов как то падает.
Здравствуйте, Геннадий Васильев, Вы писали:
ГВ>Хотя, надо сказать, что ты сейчас сравниваешь подход и конкретное решение. При моём подходе общая функция форматирования будет вырожденной, т.е., не содержащей кода форматирования. Последний будет вынесен в отдельные функции, специфические для каждого типа (скорее всего — методы).
Я не понимаю главного. Сейчас интерфейс клиентского курсора, грубо говоря, выглядит так:
class DataTable {
public DataRowCollection Rows {get;}
public DataColumnCollection Columns {get;}
}
class DataColumnCollection {
public int Count {get;}
public DataColumn this[int index] {get;}
}
class DataColumn {
public string ColumnName {get;}
public Type DataType {get;}
}
class DataRowCollection {
public int Count {get;}
public DataRow this[int index] {get;}
}
class DataRow {
public object this[DataColumn] {get; set;}
}
Как он изменится, если мы ставим целью избавиться от object и Type?
Здравствуйте, VladD2, Вы писали:
_>>Кстати, к msvc 1.0 такой был VD>В смысле макросы? Или я что-то пропустил?
Какая-то примочка к компилятору, которая брала на вход не то шаблоны с убогим синтаксисом, не то что-то свое и генерила специализации. К счастью, ее я тоже пропустил.
1. Как будет выглядить код, который должен работать с переменными m_Server и m_ProcessName?
2. Как будет выглядить код, если часть из этих полей надо еще за-save-ить?
зы
В идеале хочется вот такой код:
public TestA
{
[Editable]
string Server;
[Editable, Serializable]
string ProcessName;
typedef (select Field from TestA.Fields where Serializable in Field.Attributes) SerializablePropertiesMap;
typedef (select Field from TestA.Fields where Editable in Field.Attributes) EditablePropertiesMap;
}
Вот к такому код из C++ даже со всеми ухищрениями Александреску не получится приблизится.
.Net позволяет приблизится к такому коду, но к сожалению в Runtime, а не при компиляции
Почему данный код лучше, чем явное задание map-ов, здесь уже пробегало: это атомарность добавления/удаления
отдельного свойства, что положительно сказывается на масштабируемости классов.
Здравствуйте, VladD2, Вы писали:
VD>Короче, у меня нет желания бесполезно дискутировать о значении термина менеджед-код. Думаю, то и все остальные все поняли как надо.
Здравствуйте, VladD2, Вы писали:
VD>Это уже снобизм и шовинизм. Мне почему-то знание С++ не мешает писать на шарпе. Я еще и С знаю. И на нем много писал. Этак мы дойдем, что все кто знают старые зыки на новые уже могзги переглючить не могут.
Это ты, поскольку ты не на одном С++ писал всю жизнь. Я о тех кто серьезно использовал в работе один язык. Да и сам ты поначалу очень сильно против дотнета возражал, достаточно почитать твои споры с мной и IT на эту тему полуторагодичной давности . А вот у новичков этого нет, они сразу воспринимают особенности шарпа как единственно верные.
В общем это давно известный эффект. Ранее много народу наступило на грабли, когда изучать структурное программирование после васика было тяжелее нежели изучать его с нуля.
Здравствуйте, VladD2, Вы писали:
_>>У них все еще хуже — они даже не позволяют value-типу быть параметром шаблона. Виртуальная машина не меняется, и шаблоны остаются чисто синтаксической конструкцией. Они не могут поднять производительность, а могут только избавить от явного приведения типов и сделать код коллекций более безопасным. VD>Ну, хоть так. Хотя конечно это криво. Кстати, где об этом почитать можно (более менее конкретную ссылку, не java.sun.com).
Кстати, они дразнят обещаниями когда-нибудь добавить "вариантность" — аналог ковариантности массивов в .net, то есть возможность по желанию разрешить встроенное преобразование ArrayList<B> к ArrayList<A>, если B — наследник A. Непонятно, как это будет сделано, потому что в массивах типобезопасность при ковариантности обеспечивается встроенными проверками типа элемента при присваивании, а в шаблонах хотелось бы для производительности обходиться без этого.
Здравствуйте, VladD2, Вы писали:
VD>Здравствуйте, alexkro, Вы писали:
A>>Спорный вопрос. C# generics ничего общего с шаблонами не имеют.
VD>Да? Ты хоть гуру CLI-gyro (проект МС-ресерча добавляющий generics к SSCLI)? Один к одному шаблоны. Только слегка упрощенные, ну, и идея шаблонов развита так что шаблонными можно делать даже интерфейсы.
1) Generic параметр обладает только методами класса System::Object. Возмем следующий класс:
public class Accumulator< T > {
private T val_;
public void Add( T x ) {
val_ = val_ + x; // oops! what +?
}
}
Приходится обходить это ограничение с помощью constraints:
public interface Addable< T > {
void Add( T x, T y );
}
public class Accumulator< T, AddT > where AddT : Addable< T > {
private T val_;
public void Add( T x ) {
AddT addt = new AddT(); // even this is impossible
val_ = addt.AddT( val_, x );
}
}
public class AddableInt : Addable< int > {
public void Add( int x, int y ) { return x + y; }
}
// now I can do this
Accumulator< int, AddableInt > acc;
Но даже такую простую вещь трудно реализовать с помощью generics! Из-за невозможности вызова new для generic типа.
Хотя и это можно обойти, но так (compromises, compromises):
public class Accumulator< T, AddT > where AddT : Addable< T > {
private T val_;
private AddT addt_;
public Accumulator( AddT a ) { addt_ = a; } // come on!public void Add( T x ) {
val_ = addt_.Add( val_, x ); // virtual function call!
}
}
Но тогда, где же приемущество перед следующим, более простым вариантом:
public class Accumulator< T > {
private T val_;
private Addable< T > addt_;
// ...
}
А на C++ легко и элегантно:
template < typename T >
class Accumulator
{
T val_;
public: Accumulator< T > & operator+=( T const & x ) {
val_ = val_ + x; // even val_ += x;
// this call will be resolved at compile timereturn * this;
}
};
Ta da!
2) Generics имеют constraints. Зачем, спрашивается? А вследствие (1).
Опять же, в C++ template параметры обычно должны удовлетворять каким-либо требованиям (requirements (per Standard)). В C# — это constraints, базирующиеся на интерфейсах. Например, возьмем такое требование, как "сравнимые" для класса Derived (:public Base). В C++ это выразится любым из следующих методов:
bool operator<( Derived const & a, Derived const & b );
или так:
bool operator<( Derived a, Derived b );
или так:
bool operator<( Base & a, Base & b );
Пойди, попробуй выразить это через интерфейсы.
3) Нет typedef, значит затрудненно написание обобщенных алгоритмов таких, как (C++):
template< typename Iter >
Iter find( Iter begin, Iter end
, typename Iter::value_type const & what
// this is impossible in C# with generics,
// because value_type is a typedef
) {
for ( Iter it = begin; it != end; ++it )
if ( * it == what )
break;
return it;
}
// usage
vector< int > v;
vector< int >::iterator pos = find( v.begin(), v.end(), 5 );
Черт! Что-же делать? Ага, вот так:
public class Finder< EnumT, ElemT, CompT > where
EnumT : IEnumerator< ElemT >
, CompT : IComparable< ElemT >
{
public Finder( CompT comp ) { comp_ = comp; }
public EnumT Find( EnumT it, ElemT x ) {
while ( it.MoveNext() )
if ( ! comp_.CompareTo( it.Current, x ) )
break;
return it;
}
}
// usageclass CompareInt : IComparable< int >
{
public int CompareTo( int x, int y ) { return x - y; }
}
// Enumerator< int > should be already defined
ArrayList< int > v;
Finder< Enumerator< int >, int, CompareInt > find = new Finder < Enumerator< int >, int, CompareInt >( new CompareInt() );
Enumerator< int > pos = find.Find( v.GetEnumerator(), 5 );
И результат: в C# в три раза больше generic параметров при меньшей функциональности и гораздо более неудобном использовании. К тому же MoveNext, Current и CompareTo — это опять-же виртуальные вызовы.
A>> Пока что я для них одно применение вижу — типизированные контейнеры.
VD>Почему только контейнеры? Любые алгоритмы толерантные к типам можно будет выражать с их помощью. Хотя бесспорно, что коллекции — это самое важное место где можно применять шаблоны.
Алгоритмы, типа того, что наверху. После написания второго такого, C++ покажется милым и хорошим .
A>> Никакой новой парадигмы программирования они не откроют.
VD>А новой парадигмы и не нужно. Нужно расширение дотнета позволяющее писать более эффективные и безопастные приложения. Парадигмы же достаточно и той что есть.
Очень уж эффективным не получится. Остаются виртуальные вызовы, к которым приводит наличие constraints.
A>> Даже такая простая вещь как policy-based programming не станет возможна в C#.
VD>Что ты под этим понимаешь?
Здравствуйте, VladD2, Вы писали:
WH>>>Например в моей текущей практике почти половина ошибок на совести компилятора.(BCB6) ГВ>>Ну... плохой компилятор, что тут можно сказать. VD>А у меня в основном на моей совести.
Просто я научился писать так что большинство логичиских ошибок становятся ошибками компиляции те сводятся на уровень опечаток которые я за ошибки не считаю.
К стати за VC++7.1 я еще не замечал не верно сгенерированого кода. Так что на нем все мое. Правда и средства контороля выше.
... << RSDN@Home 1.1 alpha 1 >>
Пусть это будет просто:
просто, как только можно,
но не проще.
(C) А. Эйнштейн
Здравствуйте, alexkro, Вы писали:
A> val_ = addt_.Add( val_, x ); // virtual function call!
Нет. При раскрытии шаблона джит видит, что AddableInt::Add невиртуальный — невиртуальные методы, перекрывающие методы интерфейсов, видны в мсиле как virtual final.
A>2) Generics имеют constraints. Зачем, спрашивается? А вследствие (1).
Вследствие того, что в мсил компилится код шаблона, а не его специализации. Плюсовая концепция, где + в шаблоне может раскрыться и в T::operator+(const T&), и в ::operator+(const T&, const T&), и даже в + для типов, к которым может преобразовываться T, здесь невозможна в принципе.
А>2. Как будет выглядить код, если часть из этих полей надо еще за-save-ить?
Код был приведен для случая, когда все поля сохраняются — там параметром шаблона класс Ser (typedef RegistrySerializer Ser указан. Если какое-то поле не надо сохранять, то вместо него указывается NopSerializer. То же и для DDX — там просто 0 вместо функции надо передать. Есть еще параметр для DDV, там по дефолту NOPValidator.
Кстати, typedef ddx::members<TYPELIST_2(m_Server, m_ProcessName)> members_info; там избыточен, можно было бы сразу переменную объявлять, это просто моя недоработка.
А>зы А>В идеале хочется вот такой код: А>
А>public TestA
А>{
А> [Editable]
А> string Server;
А> [Editable, Serializable]
А> string ProcessName;
А> typedef (select Field from TestA.Fields where Serializable in Field.Attributes) SerializablePropertiesMap;
А> typedef (select Field from TestA.Fields where Editable in Field.Attributes) EditablePropertiesMap;
А>}
А>
А>Вот к такому код из C++ даже со всеми ухищрениями Александреску не получится приблизится.
Да, возможности получать в компил-тайме список членов-данных класса и пробежаться по нему в C++ сильно не хватает (лично мне, по крайней мере ). Остальное легко можно было бы сделать на шаблонах.
А>Почему данный код лучше, чем явное задание map-ов, здесь уже пробегало: это атомарность добавления/удаления А>отдельного свойства, что положительно сказывается на масштабируемости классов.
Ну у меня добавление/удаление свойстов тоже практически атомарны — забудешь нужный typdef или не внесешь поле в список, получишь ошибку компиляции. В принципе, можно было бы убрать и тайпдефы для полей, но тогда усложнился бы доступ к полям и потребовался бы более продвинутый компилятор.
Одним из 33 полных кавалеров ордена "За заслуги перед Отечеством" является Геннадий Хазанов.
Здравствуйте, Sergey, Вы писали:
S>Угу, только у класса TestC есть десяток членов, которые сериализовать не надо. По этому атрибут [Serializable] придется для каждого члена указывать,
А>>2. Как будет выглядить код, если часть из этих полей надо еще за-save-ить?
S>Код был приведен для случая, когда все поля сохраняются — там параметром шаблона класс Ser (typedef RegistrySerializer Ser указан. Если какое-то поле не надо сохранять, то вместо него указывается NopSerializer. То же и для DDX — там просто 0 вместо функции надо передать. Есть еще параметр для DDV, там по дефолту NOPValidator.
S>Кстати, typedef ddx::members<TYPELIST_2(m_Server, m_ProcessName)> members_info; там избыточен, можно было бы сразу переменную объявлять, это просто моя недоработка.
Неудобно. Получается, что сохранение и редактирование не ортогональны друг другу.
Если кроме задач "редактирования" и "сохранения" появится еще одна сходная задача:
например, вывод внутреннего состояния класса (например, надо сохранять все поля кроме ссылок "наверх", чтобы не было зацикливания) в log-файл, то как изменится код?
Здравствуйте, DarkGray, Вы писали:
S>>Код был приведен для случая, когда все поля сохраняются — там параметром шаблона класс Ser (typedef RegistrySerializer Ser указан. Если какое-то поле не надо сохранять, то вместо него указывается NopSerializer. То же и для DDX — там просто 0 вместо функции надо передать. Есть еще параметр для DDV, там по дефолту NOPValidator.
S>>Кстати, typedef ddx::members<TYPELIST_2(m_Server, m_ProcessName)> members_info; там избыточен, можно было бы сразу переменную объявлять, это просто моя недоработка.
DG>Неудобно. Получается, что сохранение и редактирование не ортогональны друг другу.
Ну это смотря как смотреть на вещи Считай, что ты объявляешь переменные и задаешь для каждой список обязательных атрибутов — что использовать для сериализации, для DDX и для DDV. Атрибуты предопределенные, о них должна заранее знать библиотека. Наверное, можно при желании сделать расширяемый список атрибутов, но мне в данном конкретном случае это просто не было нужно, и я не стал на эту тему думать. Да и компилятор для этого, боюсь, более другой потребовался бы. По крайней мере, примерно с тем же кодом, но основанным на boost::mpl мой VC 6 не справился. Можно еще было бы поизвращаться на предмет занесения в список не самих переменных, а ссылок на них, но тогда пришлось бы инициализировать этот список в конструкторе класса, да и опять таки это решение выходило бы за рамки моих текущих задач.
DG>Если кроме задач "редактирования" и "сохранения" появится еще одна сходная задача: DG>например, вывод внутреннего состояния класса (например, надо сохранять все поля кроме ссылок "наверх", чтобы не было зацикливания) в log-файл, то как изменится код?
Здравствуйте, desperado_gmbh, Вы писали:
_>Здравствуйте, alexkro, Вы писали:
A>> val_ = addt_.Add( val_, x ); // virtual function call!
_>Нет. При раскрытии шаблона джит видит, что AddableInt::Add невиртуальный — невиртуальные методы, перекрывающие методы интерфейсов, видны в мсиле как virtual final.
Твое предположение о том, как generics будут работать в runtime не верно. Следовательно, и вывод о невиртуальности вызова тоже.
A>>2) Generics имеют constraints. Зачем, спрашивается? А вследствие (1).
_>Вследствие того, что в мсил компилится код шаблона, а не его специализации.
Да ну! Только что ты сказал, что jit раскрывает generic для специализации AddableInt.
Здравствуйте, alexkro, Вы писали:
A>Твое предположение о том, как generics будут работать в runtime не верно.
Почему неверно? А как будет верно?
_>>Вследствие того, что в мсил компилится код шаблона, а не его специализации.
A>Да ну! Только что ты сказал, что jit раскрывает generic для специализации AddableInt.
Здравствуйте, <Аноним>, Вы писали:
AVK>>А для сохранения реальных данных предназначена не сериализация, а СУБД.
А>А как передавать такие объемы данных клиенту?
А вот то что "такие" объемы данных передаются клиенту свидетельствует о неверной архитектуре. Клиент, ака пользователь, способен воспринимать данные очень медленно, настолько медленно что даже куча служебной информации не способна сильно увеличить этот объем (мы не говорим конечно об аудио/видеопотоках). И если у тебя 100 тыс. записей отдаются клиенту это явный косяк.
А>В этой ветке где-то был разговор про SOAP, так что вот конкретный пример. А передать по низкоскоростному каналу связи лишние несколько метров как-то не хочется. А>По моему в RSDN как раз была статься по ручной сереализации dataset для решения этих проблем.
Это уже совершенно отдельный разговор, к самой идее сериализации не имеющий отношения. Это уже проблемы конкретных особенностей xml, soap и реализации мсовских форматтеров.
А>PS Да, и на моей практике, если менялись типы данных или запросов, то все равно менялась логика, поэтому прелесть определения типов как то падает.
Здравствуйте, alexkro, Вы писали:
A>>> val_ = addt_.Add( val_, x ); // virtual function call! _>>Нет. При раскрытии шаблона джит видит, что AddableInt::Add невиртуальный — невиртуальные методы, перекрывающие методы интерфейсов, видны в мсиле как virtual final. A>Твое предположение о том, как generics будут работать в runtime не верно. Следовательно, и вывод о невиртуальности вызова тоже.
Не неверно, а сомнительно. По крайней мере ничего не мешает это сделать не в этой версии, так в следующей.
A>>>2) Generics имеют constraints. Зачем, спрашивается? А вследствие (1). _>>Вследствие того, что в мсил компилится код шаблона, а не его специализации. A>Да ну! Только что ты сказал, что jit раскрывает generic для специализации AddableInt.
Компилятор делает msil, jit делает из него машинный код. В мсиле шаблоны лежат нераскрытыми, иначе мы не могли бы затребовать специализацию из другой сборки.
Здравствуйте, VladD2, Вы писали:
ГВ>>К типам данных источника, надеюсь? Если так, то тривиально. Выбором нужного агрегата из набора имеющихся и привязкой его к позиции колонки. switch/case остаётся только в самом начале этой цепочки — при анализе SQL-типа. Хотя можно обойтись и без него — std::map<sql-type, editor*>, например. И в чём проблема?
VD>Проблема (у тебя ) в том, что мой грид будет доступен в дизайнере и я настрою колонки за несколько секунд, а ты будешь длбить код как пака крло.
На каком основании такие предположения? Единственное, что придётся сделать — написать mapping колонок, т.е. — что-то вроде:
Притом такой маппинг описывать проще, чем прыгать по десяти Property-box-ам. Так что, кто из нас будет что-то-там как папа Карло — ещё не известно.
Да и то, его (mapping, а вернее — layout) можно настроить автоматически по списку колонок рекордсета.
VD>Только и всего. Ну, и еще проблема в том, что тебе потребуется контрол с исходными кодами.
Ещё круче. Это-то зачем? Обычного SysListView32 вполне хватит.
VD>А я могу взять готовый из очень большого списка. Ну и главная проблема в том, что твой подход потребует от пользователя твоего грида (тоже программиста) на порядок больших знаний чем у меня.
My God! Да почему?!
VD>Но ведь задача то решается одна и та же. Стало быть ты вибираешь не верный метод решения задачи.
VD>PS
VD>Глупо спорить, что если задача легко решается без напряга без ртти и т.п., то и не следует его использовать. Вот только это не всегда так.
В данном случае — пока скорее так, чем не так.
Я знаю только две бесконечные вещи — Вселенную и человеческую глупость, и я не совсем уверен насчёт Вселенной. (c) А. Эйнштейн
P.S.: Винодельческие провинции — это есть рулез!
Здравствуйте, AndrewVK, Вы писали:
AVK>>>Ладно, едем дальше. Каким образом ты собираешься привязывать верификаторы+конверторы к типам? ГВ>>К типам данных источника, надеюсь? AVK>Естественно. ГВ>>Если так, то тривиально. Выбором нужного агрегата из набора имеющихся и привязкой его к позиции колонки. AVK>Вот вот, выбор ты как будешь осуществлять и из чего?
"Из чего" — из набора прекомпилированных конвертеров.
ГВ>> switch/case остаётся только в самом начале этой цепочки — при анализе SQL-типа. Хотя можно обойтись и без него — std::map<sql-type, editor*>, например. И в чём проблема?
AVK>Проблема в том что этот самый std::map при наличии rtti не нужен.
Да ну? А как же run-time analysis: "Если символ типа — X, то использовать алгоритм Z"? Или мы с тобой подразумеваем под rtti разные вещи.
AVK>ОК, если действительно не понимаешь или делаешь вид, то пускай это будет веб-сервис. Так понятнее?
Давай ещё проще — пусть это будет просто SOAP-овский вызов, который возвращает некий массив.
ГВ>> Если очень приспичит, то я сделаю единый для клиентского приложения набор SOAP-сообщений, к которым и сведу обмен между приложением и клиентом. AVK>А если сервис не ты писал? А если он изменился?
В каком смысле изменился?
ГВ>>И при чём тут распознавание большого количества типов? AVK>При том что SOAP отнюдь не ограничен стандартным набором типов.
То есть, ты хочешь узнать, что я буду делать, если вместо
Я правильно предположил?
ГВ>>Или ты имеешь ввиду, например, что для гридов Order и Employee, буде они получены через SOAP нужно обязательно писать два отдельных грида? AVK>Нет, я имею ввиду что для отображения недетерминированных заранее источников данных rtti очень полезен.
Это тезис понятен.
Я знаю только две бесконечные вещи — Вселенную и человеческую глупость, и я не совсем уверен насчёт Вселенной. (c) А. Эйнштейн
P.S.: Винодельческие провинции — это есть рулез!