1С с "плюсами"
Разработка - Разработка внешних компонент
- Проведено документирование основных типов данных (объектов, интерфейсов) платформы.
- Разработан объект «Делегат» для реализации функциональных объектов (указатель-на-функцию).
- Разработана концепция итератора произвольного доступа для основных контейнеров платформы с целью сопряжения со стандартной C++ библиотекой.
- В процессе разработки механизм 1С классов (наследование, интерфейсы) для возможности использования парадигм объектно-ориентированного программирования на уровне кода.
И еще, тезисно:
- Файлов к скачиванию здесь пока не будет. Все еще весьма сыро. Основной целью публикации является дискуссия.
- Тесты проводились для win32::v8.3.4.365 - v 8.3.6.2237. Для win64, lin32, lin64 и для v8.2.19.130 только оценена возможность – работать будет при минимальных изменениях.
- В процессе экспериментов ни один проприетарный байт не пострадал. Т.е. оригинальные бинарники 1С я не патчу ни на диске, ни в памяти.
- Все исключительно в познавательных целях и с целью модификации программы для улучшения ее характеристик в соответствии с ее предназначением. Все имена вымышлены и совпадения случайны.
- 1С начал изучать вплотную недавно, много еще не знаю. До этого VB++C/C++. Целью работ был перенос части возможностей VB в 1С.
- Также заранее прошу прощения за стиль изложения – эпистолярный жанр не мой конек.
Итак...
Решаем простейшую задачу: Есть ТаблицаЗначений и в ней колонки [количество] и [цена]. Нужно вычислить СУММА([количество] * [цена]).
Здесь и далее время выполнения алгоритма засекается через ::GetTickCount(), т.е. является относительной величиной.
Код 1С:
Функция СчитатьТаблицу(Котейнер)
перем элем;
перем итого;
итого = 0;
для каждого элем из Котейнер цикл
итого = итого + элем[1] * элем[2];
КонецЦикла;
возврат итого;
КонецФункции
Код Си (иллюстративно):
...
cRows = pITable->getRowCount();
for (iRow = 0; iRow < cRows; iRow++)
{
if (pITable->getValueAt(iRow, 1).getIValue()->getNumeric(numQty) &&
pITable->getValueAt(iRow, 2).getIValue()->getNumeric(numPri))
{
numTot += numQty * numPri;
}
}
pOutRetValValue->assign(numTot);
...
Результаты:
*----------- сумма произведений по ТаблицеЗначений ---------------
* строк = 100 000
* посчитано СУММА([кол]*[цена]) в Си: результат = 333 328 333 350 000, время = 94
* посчитано СУММА([кол]*[цена]) в 1С: результат = 333 328 333 350 000, время = 547
* ИТОГ: подсчет в Си быстрее чем в 1С в 5,8 раз.
*----------- ------------------------------------- ---------------
Обратите внимание, что я работаю методами платформы с типами данных платформы и разница не на ...%, а в ...раз.
Сортируем..
Для опытов возьмем простейший алгоритм сортировки вставками. Исходные данные – массив случайных чисел. Простейшие алгоритмы будут безусловно сортировать по возрастанию. Также сделаем функции, принимающие предикат.
1С:
Процедура СортироватьМассивВставкамиВозр(М)
перем колво;
перем к;
перем к_этот;
перем к_пред;
перем елмЭтот;
перем елмПред;
колво = М.Количество();
для к = 1 по колво - 1 цикл
к_этот = к;
пока к_этот >0цикл
к_пред = к_этот -1;
елмЭтот = М[к_этот];
елмПред = М[к_пред];
если елмЭтот < елмПред тогда
М[к_этот]= елмПред;
М[к_пред]= елмЭтот;
иначе
Прервать;
КонецЕсли;
к_этот = к_пред;
КонецЦикла;
КонецЦикла;
КонецПроцедуры
Си:
...
for(int i = 1; i < iSize; i++){
for(int j = i; j > 0; j--){
mValThis = pParaArrIArr->getAt(j);
mValPrev = pParaArrIArr->getAt(j - 1);
if( mValThis < mValPrev){
pParaArrIArr->setAt(j - 1, mValThis);
pParaArrIArr->setAt(j, mValPrev);
}
else{
break;
}
}
}
...
Для Си, конечно, несколько многословно, но я намеренно показываю, что код переносится чуть-ли не один в один.
С++:
...
v8com_array_iterator _First(pParaArrIArr, 0);
v8com_array_iterator _Last(pParaArrIArr, pParaArrIArr->size());
std::sort(_First, _Last);
...
1С с предикатом:
процедура СортироватьМассивВставкамиПред(М, Предикат)
перем колво;
перем к;
перем к_этот;
перем к_пред;
перем елмЭтот;
перем елмПред;
перем елмЭтотМеньшеПред;
колво = М.Количество();
для к =1по колво -1цикл
к_этот = к;
пока к_этот >0цикл
к_пред = к_этот -1;
елмЭтот = М[к_этот];
елмПред = М[к_пред];
выполнить("елмЭтотМеньшеПред = " + Предикат + "(елмЭтот, елмПред)");
если елмЭтотМеньшеПредтогда
М[к_этот]= елмПред;
М[к_пред]= елмЭтот;
иначе
Прервать;
КонецЕсли;
к_этот = к_пред;
КонецЦикла;
КонецЦикла;
КонецПроцедуры
В параметр предикат передаем "МодТест.СравнитьЗначенияНаМеньше"
Функция СравнитьЗначенияНаМеньше(знач знач1, знач знач2) экспорт
возврат ?(знач1 < знач2, истина, ложь);
КонецФункции
Для компоненты с предикатом посложнее... Делаем объект «Делегат»:
перем<делегат>;
<делегат> = <компонент>.СоздатьДелегат(<объект>[.ЭтотОбъект],“<имя-метода>”);
<делегат>.Вызвать(<список-параметров-метода>);
Т.е. создаем в компоненте объект инициализируемый ссылкой на объект (модуль) платформы и строкой с указанием, какой из методов (функций или процедур) вызвать с переданными при вызове параметрами. У этого объекта-делегата единственное назначение – передать параметры и управление в указанный метод объекта платформы (и вернуть результат, если это функция). Платформа не поддерживает конструкции вида <имя-переменной>(...), поэтому, после некоторых раздумий, я решил не фиксировать какой-либо определенный синтаксис. Делегат можно .Вызвать(...), .Выполнить(...), .Обработать(...), хоть .СделайМнеОоЛаЛа(...), в зависимости от семантики.
Процедура ТестДелегата()
сообщить("*---------- ТестДелегата -----------");
хДел = СПП.СоздатьДелегат(МодТест,"ТестДелегата_Функция");
хРез = хДел.Вызвать(202);
сообщить("* Делегат вызван, результат: " + хРез);
сообщить("*---------- ------------ -----------");
КонецПроцедуры
Функция ТестДелегата_Функция(пара)экспорт
сообщить("* вызов делегата, аргумент = "+ пара);
возврат пара +1;
КонецФункции
*---------- ТестДелегата -----------
* вызов делегата, аргумент = 202
* Делегат вызван, результат: 203
*---------- ------------ -----------
С учетом этого код сортировки плюсами c предикатом выглядит как то так:
...
v8com_array_iterator _First(pParaArrIArr, 0);
v8com_array_iterator _Last(pParaArrIArr, pParaArrIArr->size());
v8com_compare_callback _Comp(pParaIDelegat);
std::sort(_First, _Last, _Comp);
...
Результаты:
*-------------------- сортировка массива ---------------------
*Количество элементов массива: 1 500
*{802, 974, 846, 95, 355, 377, 933, 222, 968, 64, 198, 395, 966, ..., 136, 611, 161}
* сортировка 1С вставками, возр. == 3 078
* сортировка Си вставками, возр. == 329
* сортировка С++ вставками, возр. == 235
* сортировка С++ std::sort, возр. == 17
* С++ std::sort с предикатом, возр. == 94
* Си вставками с предикатом, возр. == 2 064
* 1С вставками с предикатом: == 56 969
*
*Проверка массививов: ОК
*{1, 1, 2, 2, 3, 3, 4, 5, 5, 6, 7, 7, 8, ..., 992, 993, 994, 996, 996, 997, 997, 999}
*
* С вставками быстрее, чем 1С вставками в 9,4 раз
* С++ вставками быстрее, чем 1С вставками в 13,1 раз
* С вставками быстрее, чем С++ вставками в 0,7 раз
* С++ сорт быстрее, чем 1С вставками в 181,1 раз
* С++ сорт быстрее, чем С вставками в 19,4 раз
* С++ сорт быстрее, чем С++ вставками в 13,8 раз
* С++ сорт быстрее, чем С++ сорт с предикатом в 5,5 раз
* С++ сорт с предикатом быстрее, чем 1С сорт вставками в 32,7 раз
* С сорт вставками с предикатом быстрее, чем 1С сорт вставками в 1,5 раз
* 1С вставками с предикатом медленнее, чем 1С сорт вставками в 18,5 раз
* 1С вставками с предикатом медленнее, чем Си вставками с предикатом в 27,6 раз
* 1С вставками с предикатом медленнее, чем С++ сорт с предикатом в 606,1 раз
*-------------------- ------------------ ---------------------
Замечу, что количество элементов массива подобрано для получения некоторых характерных значений. Если их порядка 300, то с std::sort сравнить не получается – там просто 0. Если порядка 5000, то ждать пока 1С отсортирует с предикатом приходится неприемлемо долго. Графики в зависимости от количества элементов массива, конечно, были бы любопытны. Также интересно поведение std::_Insertion_sort(), влияние InterlockExhangeXXX, сравнение с контейнером над тривиальными типами данных, сравнение разных компиляторов, поведение в режиме отладки, т.д., т.п. При наличии времени и интереса это все, конечно, можно сделать.
Общие выводы:
1) Любой алгоритм, перенесенный чуть ли не один в один из 1С в компоненту, оживляется на порядок. С теми же родными типами данных платформы и вычисленным собственным же АПИ платформы.
2) Любой простенький алгоритм, написанный на языке 1С, несравнимо проигрывает вылизанному алгоритму из существующей С/С++ библиотеки, коих миллион.
Ну и в заключение, коль скоро у нас есть Делегат, можно похулиганить с полиморфизмом:
// Зоопарк делегатов
процедура ТестДелегатовЗоо() экспорт
перем мойЗоопарк;
мойЗоопарк =новый Массив;
мойЗоопарк.Добавить(СоздатьСтруктуруЖивотное_Кошка("Мурка"));
мойЗоопарк.Добавить(СоздатьСтруктуруЖивотное_Собака("Шарик"));
ТестДелегатовЗоо_ОпроситьЗоопарк(мойЗоопарк);
КонецПроцедуры
Функция СоздатьСтруктуруЖивотное_Собака(Имя)
возврат СоздатьСтруктуруЖивотное(Имя, СоздатьДелегат(МодТест,"ЖивотноеСобака_Голос"));
КонецФункции
Функция СоздатьСтруктуруЖивотное_Кошка(Имя)
возврат СоздатьСтруктуруЖивотное(Имя, СоздатьДелегат(МодТест,"ЖивотноеКошка_Голос"));
КонецФункции
Функция СоздатьСтруктуруЖивотное(Имя, Голос)
возвратновый ФиксированнаяСтруктура("Имя, Голос", Имя, Голос);
конецфункции
Функция ЖивотноеКошка_Голос() экспорт
возврат"Мяу";
КонецФункции
Функция ЖивотноеСобака_Голос() экспорт
возврат"Гав";
КонецФункции
процедура ТестДелегатовЗоо_ОпроситьЗоопарк(З)
перем Ж;
сообщить("*");
сообщить("*------- Перекличка в зоопарке делегатов -----------");
длякаждого Ж из З цикл
Сообщить("* Животное '" + Ж.Имя + "' отозвалось '" + Ж.Голос.Вызвать() + "'");
КонецЦикла;
сообщить("*------- ------------------------------- -----------");
сообщить("*");
КонецПроцедуры
*------- Перекличка в зоопарке делегатов -----------
* Животное 'Мурка' отозвалось 'Мяу'
* Животное 'Шарик' отозвалось 'Гав'
*------- ------------------------------- -----------
Все-таки структура с Делегатом это не совсем класс. Все равно опять .Вызвать() да .Выполнить(). Хочется полноценных интерфейсов, наследования, виртуальных методов и т.д. и т.п.
//Зоопарк классов
функция ПолучитьОписательКлассаЖивотное()
перем К;
К = СоздатьОписательКласса("КлассЖивотное");
К.ДобавитьПоле("Имя");
К.ДобавитьПоле("м_ВремяРождения");
К.ДобавитьМетод("ДайВозраст", МодТест,"КлассЖивотное_ДайВозраст");
К.ДобавитьАбстрактныйМетод("ДайГолос");
возврат К;
КонецФункции
функция ПолучитьОписательКлассаЖивотноеКошка()
перем К;
К = ПолучитьОписательКлассаЖивотное();
К.ПереопределитьМетод("ДайГолос", МодТест,"КлассЖивотноеКошка_Голос");
возврат К;
КонецФункции
функция ПолучитьОписательКлассаЖивотноеСобака()
перем К;
К = ПолучитьОписательКлассаЖивотное();
К.ПереопределитьМетод("ДайГолос", МодТест,"КлассЖивотноеСобака_Голос");
возврат К;
КонецФункции
функция КонструкторЖивотного(Класс, Имя, ВремяРождения)
Класс.Имя = Имя;
Класс.м_ВремяРождения = ВремяРождения;
возврат Класс;
КонецФункции
функция СоздатьКлассЖивотное_Собака(Имя)
//туду: возврат ПолучитьОписательКласса("ЖивотноеСобака").Конструкторы[..].Вызвать(<список-параметров>);
// => .Вызвать(<класс-интерфейфейс-конструктора>, <параметры>)
возврат КонструкторЖивотного(СоздатьКлассПоОписателю(ПолучитьОписательКлассаЖивотноеСобака()), Имя, ДайТики()-456);
КонецФункции
функция СоздатьКлассЖивотное_Кошка(Имя)
возврат КонструкторЖивотного(СоздатьКлассПоОписателю(ПолучитьОписательКлассаЖивотноеКошка()), Имя, ДайТики()-567);
КонецФункции
функция КлассЖивотное_ДайВозраст(Класс, Еденицы) экспорт
перем х;
х = ДайТики()- Класс.м_ВремяРождения;
возврат Окр(?(Еденицы = ТипЕдиницыШкалыВремени.Минута, х/60, х),1); //чё-то для теста
КонецФункции
функция КлассЖивотноеКошка_Голос(Класс) экспорт
возврат"Мур-Мяу";
КонецФункции
функция КлассЖивотноеСобака_Голос(Класс) экспорт
возврат"Гав-Гав";
КонецФункции
процедура ТестКлассовЗоо() экспорт
перем мойЗоопарк;
мойЗоопарк =новый Массив;
мойЗоопарк.Добавить(СоздатьКлассЖивотное_Кошка("Мурка"));
мойЗоопарк.Добавить(СоздатьКлассЖивотное_Собака("Шарик"));
ТестКлассовЗоо_ОпроситьЗоопарк(мойЗоопарк);
КонецПроцедуры
процедура ТестКлассовЗоо_ОпроситьЗоопарк(З)
перем Ж;
сообщить("*");
сообщить("*------- Перекличка в зоопарке классов -----------");
длякаждого Ж из З цикл
Сообщить("* Животное '"+ Ж.Имя +"', возраст = "+ Ж.ДайВозраст(ТипЕдиницыШкалыВремени.Минута)+", отозвалось '"+ Ж.ДайГолос()+"'");
КонецЦикла;
сообщить("*------- ----------------------------- -----------");
сообщить("*");
КонецПроцедуры
*------- Перекличка в зоопарке классов -----------
* Животное 'Мурка', возраст = 9,5, отозвалось 'Мур-Мяу'
* Животное 'Шарик', возраст = 7,9, отозвалось 'Гав-Гав'
*------- ----------------------------- -----------
Пока все. Сыро, но работает. Возможности развития ограничиваются только фантазией. Примененимость повсеместная. Назрело поговорить. Масса вопросов, например, по синтаксису:
- о множественном наследовании думать или как все, кроме плюсов?
- и вообще Класс он НАСЛЕДУЕТСЯ ОТ ?, РАСШИРЯЕТ ?
- class <name> : private <base-class> - такое надо?
- this, me, self- как? В текущей реализации это просто переменная. Пишу Класс, будут конфликты можно переименовать. Но все же...
- private - ЧАСТНЫЙ? ВНУТРЕННИЙ?
- mustoverride – ПЕРЕОПРЕДЕЛЯЕМЫЙ ОБЯЗАТЕЛЬНО?
- ...
И Особенно смущает вопрос, почему 1С это все скрывает, ведь все эти возможности есть у платформы с рождения.
У кого какие мысли?
Спасибо.
<необходимое дополнение>
Я добровольно обязуюсь не использовать имеющуюся у меня информацию о механизмах платформы 1С в целях несанкционированного доступа к данным, не публиковать такую информацию и обеспечить сохранность таковой информации от несанкционированного доступа третьих лиц.
Специальные предложения
См. также
Быстрое создание наполненных коллекций 58
28.10.2019 3198 SeiOkami 51
Преобразование XML в таблицу значений или иной объект 1С методом XSL преобразования 41
24.10.2019 3156 kraspila 25
Обертка функций Excel на русском. Ускорение процесса разработки. 41
24.10.2019 2294 DmitryKotov 6
Полезняшки по СКД и построителям. Просто код 45
10.10.2019 4040 Yashazz 45
Полезные процедуры и функции для программиста 140
07.10.2019 8998 HostHost 23
Регистры бухгалтерии. Общая информация 111
05.09.2019 6738 YPermitin 22
"Хочу универсально!" [Часть 1] 65
02.09.2019 4914 SeiOkami 35
Иерархия без "В ИЕРАРХИИ" 117
22.08.2019 4921 ildarovich 16
EnterpriseData – часть 3. Загрузка данных, идентификация объектов 62
22.08.2019 4242 ids79 7
Отслеживание выполнения фонового задания 141
17.08.2019 10506 ids79 16
PinkRabbitMQ - Native API компонента 1С с открытым исходным кодом, для обмена сообщениями через RabbitMQ 137
29.07.2019 7146 445 Begemoth80 110
Обработчики событий при записи объектов. Зачем и что за чем? 202
25.07.2019 12817 4 AlbinaAAA 23
Управление качеством кода 136
22.07.2019 8267 Stepa86 29
Что делает "В ИЕРАРХИИ" в запросе? 94
16.07.2019 8213 YPermitin 34
Создание отчетов с помощью СКД - основные понятия и элементы 208
25.06.2019 21063 ids79 17
Реализуем Стек, Очередь и Приоритетную очередь в 1С 52
24.06.2019 7815 RonX01 63
Сортируем ДанныеФормыДерево на клиенте 35
18.06.2019 4386 SeiOkami 17
Вычисление 200 тысяч знаков числа pi 73
28.05.2019 3993 Oleg_nsk 93
Регистры накопления. Виртуальные таблицы. Часть №1: Обороты 84
20.05.2019 11118 YPermitin 5
Доработка проведения типовых документов в УТ 11.4, КА 2.4, ЕРП 2.4 101
22.03.2019 9566 ids79 14
Работа со строками: от простого к сложному 26
14.01.2019 8677 Evg-Lylyk 17
Разработка и сценарное тестирование с Vanessa-ADD. Концепция, теория и сквозной пример создания сценария 222
09.01.2019 27597 Vladimir Litvinenko 69
Многопоточное восстановление последовательностей 41
05.12.2018 7255 _ASZ_ 29
Универсальные функции ЗУП 3.1 / ЗКГУ 3.1, которые помогут в разработке 485
14.11.2018 35498 GeterX 93
Автоматические и управляемые блокировки применительно к типовым конфигурациям 1С 127
10.11.2018 22289 ids79 40
Кадровые данные сотрудников в ЗУП 3.1 в отчетах 39
07.11.2018 14212 fromlion 14
Основные понятия и механизмы оптимизации клиент-серверного взаимодействия в 1C 147
23.08.2018 22919 Rain88 42
Причины реструктуризации. Практический пример 39
17.08.2018 7226 _KaA 10
Минимализмы 3 355
19.02.2018 37137 ildarovich 44
Таблица значений в Таблицу HTML - функция с возможностью настройки цвета шапки, заголовков, выравнивания и размера колонок 83
22.12.2017 19600 rpgshnik 21
Пример преобразования двоичных данных в строку 26
08.12.2017 12985 frkbvfnjh 19
Введение в CI для 1С 87
21.11.2017 19335 real_MaxA 22
Определяем контекст сеанса 1С программно (Тонкий/Толстый клиенты/HTTP-Сервис/Фоновое задание и т.д.) 31
08.11.2017 16116 azubar 9
#Область ВНЕШНИЕ_ВЫЗОВЫ или MVC в 1С, библиотечность и упрощение интеграции кода 43
12.10.2017 14813 for_sale 58
Групповая разработка конфигураций в крупном холдинге 68
15.08.2017 17552 stas_ganiev 15
Разность дат 23
11.08.2017 10136 jun-ko 24
Простой способ преобразовать UNICODE в строку или в структуру 23
07.07.2017 7434 dimasts 5
Автоматизация процесса 1С-разработки 91
07.06.2017 23032 ekaruk 9
Пишем игру Минер. Обработка событий ActiveX в 1С 29
29.05.2017 12741 user621724_Dimav1979 11
Как я доступ на kb.1c.ru получал 91
01.05.2017 22539 ikekoval 33
"Распределение в запросе" или "избавляемся от перебора" 185
16.12.2016 28641 alexandersh 48
Планы обмена. Квитировать или гарантировать? 24
12.12.2016 14565 zhichkin 9
Некоторые принципы оптимизации запросов 1С (+SQL) 115
17.11.2016 8902 ture 40
Использование git для доработки типовых конфигураций 1С 230
11.10.2016 188498 pumbaE 31
Оптимизация запросов 1С:Предприятие – от теории к практике 116
07.10.2016 32089 bpc222 20
Парсер JSON (Штатные средства 1С 8.3.6) 59
29.09.2016 48010 dour-dead 21
Программное создание графических схем (v.2): API для ГрафическойСхемы 73
27.09.2016 17265 serg_infostart 15
Используем механизмы обмена данными БСП для произвольного обмена 150
23.08.2016 28375 Патриот 22
1С, Linux, Excel, Word, OpenXML, ADO, Net Core 31
22.08.2016 17303 51 Serginio 14
Склонение числа прописью 26
18.08.2016 16317 maxvcb 27