Впечатления от знакомства с Ultimate++

в 18:36, , рубрики: c++, метки:

Если вы фрилансер и любите экспериментировать с экзотическими средами под С++, то стоит обратить внимание на экосистему Ultimate++, о которой я узнал совсем недавно благодаря циклу статей Семена Есилевского (ч.1, ч.2, ч.3, ч.4 — все есть на вики) с таким финальным напутствием:

«Перевешивают ли выгоды U++ его необычность и высокий «барьер вхождения»? На мой взгляд, да. U++ – прекрасный выбор для кроссплатформенных проектов, которые разрабатываются «с нуля», особенно если предполагается интенсивное использование баз данных.»

Под катом те нюансы U++, которые удалось раскопать на форуме и в мануалах за месяц ежедневной работы над редактируемым справочником документов, хранящихся в базе. Сразу отмечу, что сравнения с «замечательной троицей» не будет, так как раньше для простейшего gui использовал WTL и с надеждой смотрел на библиотеку eGUI++, которую, к сожалению, автор забросил, а подхватить некому.

Прежде всего, Ultimate++ это ультиматум: либо собственная среда TheIDE, либо, к примеру, такой набор под Windows, как Visual Studio + STL + POCO + WTL. Как следствие, приходится забыть об исключениях, хотя в остальном ядро приличное, что иллюстрируют фрагменты написанного мною кода асинхронной клиент-серверной передачи файлов.

Ядро

  • Делегаты (основное средство для привязки событий в GUI; лично меня они зацепили больше всего)
    //ограничения: 
    //функция не должна возвращать значений, а также иметь больше 4х входных параметров
    void Func() {};
    
    void Func4(int, double, String, Value) {}
    
    //делегаты для функций
    GUI_APP_MAIN
    {	
    	Callback cb = callback(Func);
    	cb();
    
    	//stateful
    	Callback cb4 = callback4(Func4, 1, 2, AsString(3), 4);
    	cb4();
    
    	//stateless
    	Callback4<int double string value> cb1 = callback(Func4);
    	cb1(1, 2, AsString(3), 4);
    }
    
    //делегаты в классе
    class AppMain {
    public:
    	//для THISBACK
    	typedef AppMain CLASSNAME;
    	
    	Callback cb;
    	Callback cb4;
    
    	AppMain() {
    		cb = THISBACK(Func);
    		cb4 = THISBACK4(Func4, 1, 2, AsString(3), 4);
    	}
    	
    	void Func() {};
    
    	void Func4(int, double, String, Value) {}	
    };
    
    //делегаты для объектов
    GUI_APP_MAIN
    {	
    	AppMain m;
    	m.cb = callback(&m, &AppMain::Func);
    	m.cb4 = callback4(&m, &AppMain::Func4, 1, 2, AsString(3), 4);
    }
    </int>

  • Синглтон
    //класс SomeClass должен иметь конструктор по-умолчанию
    Single<someclass>().SomeMethod();
    

  • Логирование (имейте ввиду, что для вещественных чисел вместо %lf используется %f)
    //вывод на экран и в файл
    StdLogSetup(LOG_COUT|LOG_FILE);
    ...
    LOG(Format("Total %d files have sendedn", n));
    LOG("Total " << n << " files have sended");
    

  • INI-файлы
    VectorMap<string string> config = LoadIniFile(GetExeDirFile("config.ini"));
    String host = config.Get("HOST");
    int port = ScanInt(config.Get("PORT"));
    	
    for (int i = 0; i < config.GetCount(); i++) {
    	if ("FILE" == config.GetKey(i)) {
    		Cout() << config[i] << "n";
    	}
    }
    

  • Многопоточность
    //без ожидания завершения
    Thread::Start(callback(Func));
    
    //c ожиданием завершения
    Thread th;	
    th.Run(callback(Func));
    th.Wait();
    
    Thread::ShutdownThreads();	
    

    Насколько я понял, OpenMP не поддерживается, вместо этого предалагается CoWork, работающий с делегатами, что для распараллеливания циклов совсем не удобно.

  • Текстовый JSON (бинарные данные поддерживаются за счет пары функций Base64Encode/Base64Decode)
    FileIn fp(fname);
    Json json;
    json("fname", fname)("fsize", (int)fp.GetSize())("fdata", Base64Encode(LoadFile(fname)));
    	
    ValueArray jsonAr = ParseJSON(json.ToString());
    fname = jsonAr[0];
    int fsize = jsonAr[1];
    String fdata = Base64Decode(jsonAr[2]);
    

  • Сокеты
    TcpSocket server;
    if (!server.Listen(port)) {
    	LOG(Format("Can't open server port %d for listeningn", port));
    	return;
    }
    
    for(;;) {
    	LOG("Waiting...");
    
    	TcpSocket socket;	
    
    	if (socket.Accept(server))	{
    		String msg = "";			
    		for (int c = socket.Get(); c > 0 && c != 'n'; c = socket.Get()) {
    			msg.Cat(c);				
    		}
    	}
    }	
    

  • RegExp (PCRE)
    RegExp reg("(\\)");
    String path = "D:\test.txt";
    if (reg.Match(path)) {
    	int last;
    	int first;
    	reg.GetMatchPos(0, first, last);        	
    
    	String drive = path.Mid(0, last);
    	String fname = path.Mid(last);
    }
    

  • Встроенная проверка утечек памяти (malloc не отслеживается)
    GUI_APP_MAIN
    {  
    	double *d = new double(0);
    }
    

    image

SQL

Просто посмотрите сюда и сюда. Поклонники php/Yii должны оценить.

QTF (ReportView)

QTF это собственный формат U++ для расширенного оформления текста. Используется в RichEdit и при генерации отчетов. В TheIDE имеется специальный дизайнер для экспериментов:

image

Также предусмотрен специальный диалог для печати отчета (сохранение в pdf есть в недрах ReportWindow):

Color rgb_color = Color(109, 171, 211);
String qtf_color = Format("@(%d.%d.%d)", rgb_color.GetR(), rgb_color.GetG(), rgb_color.GetB());

String qtf;
qtf.Cat(Format("[R9/%s Habrahabr &]", qtf_color));
qtf.Cat(Format("[_%s лента] [ посты] [_%s q\&a] [_%s события] [_%s хабы] [_%s компании]", 
	qtf_color, qtf_color, qtf_color, qtf_color, qtf_color));
	
Report rep;
rep << qtf;
	
ReportWindow().Perform(rep);

image

Стилевое оформление текста обрамляется тэгами [ и ], причем сначала за скобкой [ идут QTF-идентификаторы, а затем только через пробел текст. В целом принцип как в HTML, разница лишь в наименованиях.

Bazaar

Bazar это набор пользовательских библиотек для специфических нужд, среди которых мне очень пригодилась кроссплатформенная работа с Word/Excel в Office Automation:

#include <OfficeAutomation/OfficeAutomation.h>

GUI_APP_MAIN
{	
	//Excel
	OfficeSheet sheet;
	
	bool xlsOn = sheet.IsAvailable("Microsoft");
	if (xlsOn)
		sheet.Init("Microsoft");

	//Open Office Calc
	if (!xlsOn) {
		xlsOn = sheet.IsAvailable("Open");
		if (xlsOn)
			sheet.Init("Open");
	}
		
	if (xlsOn) {
		FileSel fs;
		fs.Type("Файлы таблиц", "*.xls *.xlsx");
		fs.AllFilesType();
		
		if (fs.ExecuteOpen("Выберите Excel файл")) {
			sheet.OpenSheet(~fs, true);	
			sheet.AddSheet(true);
		}
	}
			
	//Word
	OfficeDoc doc;
	
	//Если установлен Word2003 и Word2007, открывается почему-то Word2003. Разработчик разводит руками.
	bool docOn = doc.IsAvailable("Microsoft");
	if (docOn)
		doc.Init("Microsoft");

	//Open Office Writer
	if (!docOn) {
		docOn = doc.IsAvailable("Open");
		if (docOn)
			doc.Init("Open");
	}
		
	if (docOn) {
		FileSel fs;
		fs.Type("Файлы Word", "*.doc *.docx *.rtf");
		fs.AllFilesType();
		
		if (fs.ExecuteOpen("Выберите Word файл")) {
			doc.OpenDoc(~fs, true);	
			doc.AddDoc(true);
		}
	}
}

Нюансы

  • Русскоязычный диалог выбора файлов не переводит вложенные папки, причем даже если сама Windows руссифицирована:
    SetLanguage(SetLNGCharset(GetSystemLNG(), CHARSET_UTF8));
    

    image

  • Если посмотреть примеры реальных приложений, то можно заметить, что там нет скриншотов с иконками и текстом в меню, потому что это сделать нельзя — иконки с текстом могут иметь только вложенные разделы меню, а главное меню всегда текстовое.
  • У кнопок нет свойств по-умолчанию для изменения размера.
  • Не поддерживается удобное блочное смещение строк через Alt+Shift+стелочки+tab, что после Visual Studio и Notepad++ напрягает.
  • Самая большая проблема редактором касается не всегда корректной отмены последней операции. Проявляется в двух ипостасях: при ручной отмене (или Ctrl+Z) соседние и не только строки могут начать перемешиваться (за день один- два раза ловится); при запуска через F5 или Ctrl+F5 отменяется, опять же иногда, последнее редактирование (случается чаще).
  • Есть проблемы с Intellisense, который охотнее отображает доступные сигнатуры уже существующей в коде переменной, чем у только что написанной. Также хотелось бы распознавания ситуаций, когда при вызове процедуры у объекта из списка код obj.f() не трансформировался бы в obj.f() ()

Резюме

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

Автор: shtr

Источник

* - обязательные к заполнению поля


https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js