26 Декабрь 2009

Fast iterating over xml nodes by MSXML

Волею судеб мне приходится иметь дело с MSXML. Не самая лучшая библиотека для работы с xml, но так вот получилось. И будучи любителем конструкции for_each, обход дочерних нод в моем коде выглядит так:
BOOST_FOREACH(IXMLDOMNodePtr Node, ChildNodes(RootNode))
DoSomething(Node);
ChildNodes отдает пару итераторов, которые дают доступ по IXMLDOMNodeList через IXMLDOMNodeList::item(). Так вот на обработке большого xml файла заметил, что такая конструкция очень жутко тормозит. Я было хотел списать все это со словами "ну этож MSXML тормозная - ничего не поделаешь...". Но как оказалось, правдива только первая часть этой фразы, а "поделать" все-таки что-то можно: если перебирать ноды через IXMLDOMNodeList::nextNode(), то вместо исходных 450мс получалось обойти ноды всего за 28:
IXMLDOMNodeListPtr NodeList = RootNode->childNodes;
for (IXMLDOMNodePtr Node; Node = NodeList->nextNode(); )
DoSomething(Node);

Видимо про такие случаи Спольски рассказывал байку про маляра, который красил забор. За первый день он покрасил 20 метров забора, за второй - 10, за третий - всего 2 (цифры привожу по памяти, память дырявая). Когда его спросили, почему он так стал тормозить, ответ был великолепен: "Насяльника, так за краской все дальше и дальше ходить!" Видимо IXMLDOMNodeList::item() работает таким же образом, как и тот маляр, так что beware!

02 Декабрь 2009

Calculating XPath expressions by MSXML

Если посчитать выражение XPath, что может получиться?
Согласно спецификации XPath:
- список узлов
- строка
- логическое значение
- число

В .NET-е можно получить все из вышеперечисленного. А вот MSXML предлагает API только для получения списка узлов - selectNodes.

А что, если нужно получать значения других типов? Товарищи в форумах предлагают хитрый трюк - обернуть XPath выражение в XSLT, наложить его на нужный узел или документ, а в результате получить строку. Даже закрывая глаза на производительность такого трюка, таким способом не удается получить всю информацию - а именно информацию о типе результата. Ведь на выходе - всегда строка.

Если выражение - "наше", то тип результата сразу знаем. Но если "чужое", и нам нужно узнать тип результата, то в таком случае мы в тупике - из полученной строки не получится извлечь тип. Вот, 123 - это строка "123" или число 123?

Я узнал о следующем - в MSXML можно использовать JavaScript внутри XSLT. Тогда все становится достаточно просто:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:msxsl="urn:schemas-microsoft-com:xslt"
xmlns:js="urn:custom-javascript"
exclude-result-prefixes="msxsl js" >
<xsl:template match="/">
<xsl:variable name="value" select="выражение"/>
<result>
<value>
<xsl:value-of select="$value"/>
</value>
<type>
<xsl:value-of select="js:TypeOf($value)"/>
</type>
</result>
</xsl:template>
<msxsl:script language="JavaScript" implements-prefix="js">
function TypeOf(strText) { return typeof strText; }
</msxsl:script>
</xsl:stylesheet>
Специальная JavaScript-функция вернет нам тип выражения, остается только не думать какая у всего этого производительность ;)

02 Ноябрь 2009

Unicode: life-hack

Мой обычный сценарий при создании новых файлов в проекте:
1. right click в Windows explorer на папке, где нужно создать файл
2. выбор Create/"New Text File.txt"
3. ввод имени файла
4. right click на файле
5. SVN/Add
6. Перетаскивание файла в IDE

Небольшой лайф-хак позволяет сразу создавать файлы в UTF-8:

Файл создается с BOM для UTF-8.

21 Октябрь 2009

Yo!

Отсортировать строки по алфавиту очень просто - все необходимые сведения о порядке символов есть в классе std::locale. Остается только надеяться что std::locale не подведет. А, как оказалось, она может. Вот пример:
struct lexicographical_order
{
std::locale Locale;

lexicographical_order(std::locale Locale) : Locale(Locale)
{
}

bool operator()(std::wstring const &lhs, std::wstring const &rhs) const
{
return boost::ilexicographical_compare(lhs, rhs, Locale);
}
};

int main()
{
std::vector v;
v.push_back(L"яблоко");
v.push_back(L"апельсин");
v.push_back(L"банан");
v.push_back(L"персик");
v.push_back(L"ёжик");

std::locale Locale(""); // используем текущую локаль системы
std::cout << Locale.name() << std::endl;
std::sort(v.begin(), v.end(), lexicographical_order(Locale));

_setmode(_fileno(stdout), _O_U16TEXT);
std::copy(v.begin(), v.end(), std::ostream_iterator(std::wcout, L"\n"));
return 0;
}

Под русской Windows получаем:
Russian_Russia.1251
ёжик
апельсин
банан
персик
яблоко

Буква "ё" явно пользуется у данной локали большей популярностью, чем все остальные. Ну прям хоть свою локаль пиши...

28 Август 2009

Boost Tuple Serialization

Сложно поверить, но в Boost-е нет сериализации для tuple.

Видимо вся фигня в том, что при сериализации кортежей нужно написать сериализацию для кортежей 0..N элементов, а это можно сделать:
а) руками: написать вручную сериализацию для 0, 1, 2 и т.д. элементов, что совсем не кошерно;
б) препроцессором: использовать boost preprocessor - тоже считается не очень кошерно;
в) компилятор: но тут вся загвоздка как я понял в том, что это нельзя сделать используя публичный интерфейс tuple, можно только зная особенности реализации (details), что тоже вроде бы не очень кошерно.

В результате был выбран списоб "г" - вообще не писать сериализацию для tuple. Что я считаю полным бредом.

PS. Если что - есть готовая реализация через препроцессор.

19 Июль 2009

Sorting by several fields

Как вы сортируете по нескольким критериям?
struct foo
{
A a; B b; C c; D d;
X x; Y y;
};

// нужно отсортировать по a, затем по b, затем по c, ну а затем еще и по d
// по x и y сортировать не нужно
bool s(foo const &l, foo const &r)
{
if (l.a < r.a) return true;
if (l.a > r.a) return false;
if (l.b < r.b) return true;
if (l.b > r.b) return false;
if (l.c < r.c) return true;
if (l.c > r.c) return false;
return l.d < r.d;
}

std::vector<foo> f;
std::sort(f.begin(), f.end(), s);

Признаюсь, я примерно так раньше и сортировал. Хотя по сути-то [a,b,c,d] - это ж по-сути обычный кортеж, а для них уже есть готовые библиотеки. И операции сравнения кортежей там должны быть, в этих библиотеках. Нужно просто загнать l и r в кортежи (только для скорости - не в [A,B,C,D], а в [const&A,const&B,const&C,const&D]), и сравнить:
#include <boost/tuple/tuple_comparison.hpp>

bool s(foo const &l, foo const &r)
{
return boost::tie(l.a, l.b, l.c, l.d) < boost::tie(r.a, r.b, r.c, r.d);
}

16 Июль 2009

Static member definition

Интересно, что статический член класса нельзя определить прямо в декларации класса:
class foo
{
static std::string s('hello world');
};
А иногда сильно этого хочется - например в декларации шаблона. Или просто в каком-нибудь header-only классе. Самое интересное, что внутри функции такое провернуть можно. Поэтому можно сделать финт ушами - сделать доступ к статическому члену через member-функцию:
class foo
{
static std::string &s()
{
static std::string impl('hello world');
return impl;
}
};
Синтаксис при обращении немного поменяется - s() вместо s, но часто оно того стоит.