Каталог решений - 1С с "плюсами"

1С с "плюсами"

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С в целях несанкционированного доступа к данным, не публиковать такую информацию и обеспечить сохранность таковой информации от несанкционированного доступа третьих лиц.

 

 

 

has been added to your cart:
Оформление заказа