/*
Yauheni A. Akhotnikau (C) 1997-2004 e-mail: eao197@yahoo.com Permission is granted to anyone to use this software for any purpose on any computer system, and to redistribute it freely, subject to the following restrictions: 1. This software is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 2. The origin of this software must not be misrepresented, either by explicit claim or by omission. 3. Altered versions must be plainly marked as such, and must not be misrepresented as being the original software. */ |
cpp_util_2: Список модификаций.
cpp_util_2: Обертки для вызова унарных методов объектов
Полный список документированных пространств имен.
Иерархия классов.
Классы с их кратким описанием.
Полный список документированных файлов.
Полный список дополнительных описаний.
Основное пространство имен проекта cpp_util_2.
Функция для конвертации значений из одного типа в другой с использованием промежуточного текстового представления и специальноых способов преобразования значения.
Данный вариант lexcast использует объект-преобразователь, который осуществляет преобразование значения в std::ostream и объект-преобразователь, который считывает значение из std::istream.
Если требуется только специальный объект-getter, то в качестве объекта-putter можно указать объект cpp_util_2::lexcasts::def_putter:
// Извлекаем число из строкового шестнадцатиричного представления.
int i = cpp_util_2::lexcast< int >( "ffab", cpp_util_2::lexcasts::def_putter(), cpp_util_2::lexcasts::hex_getter() ); |
void
operator( std::ostream & to, const From & what ) const; |
void
operator( std::istream & to, To & what ) const; |
Функция для конвертации значений из одного типа в другой с использованием промежуточного текстового представления и специального способа преобразования значения.
Данный вариант lexcast использует объект-преобразователь, который осуществляет преобразование значения в std::stringstream.
void
operator( std::ostream & to, const From & what ) const; |
Функция для конвертации значений из одного типа в другой с использованием промежуточного текстового представления.
В ряде случаев возникает задача преобразования некоторого значения в его текстовое представление. Например, при порождении исключений:
// Способ 1. Использование sprintf.
const std::string & my_string_vector_t::operator[]( unsigned int index ) const { if( index >= size() ) { // Преобразуем index в строку, чтобы можно // было сформировать подробное описание ошибки. char tmp[ 32 ]; sprintf( tmp, "%u", index ); throw std::out_of_range( std::string( "index is too big" ) + tmp ); } ... } // Способ 2. Использование std::ostringstream. const std::string & my_string_vector_t::operator[]( unsigned int index ) const { if( index >= size() ) { // Преобразуем index в строку, чтобы можно // было сформировать подробное описание ошибки. std::ostringstream tmp; tmp << index; throw std::out_of_range( std::string( "index is too big" ) + tmp.str() ); } ... } |
Оба этих способа усложняют код не нужными подробностями преобразования одного значения в строковое представление. И, хотя способ с sprintf более эффективен, он более сложен в сопровождении, т.к. формат sprintf жестко связан с типом параметра index. Если со временем index поменяет тип (на unsigned long для очень больших массивов или на std::string для ассоциативных массивов), то придется помнить про особенность форматной строки sprintf. В случае с std::ostringstream такой проблемы нет.
Функция lexcast() упрощает подобные преобразования, сохраняя все достоинства и недостатки способа с применением std::ostringstream. В приведенном примере функция lexcast() может быть использована следующим образом:
// Способ 3. Использование cpp_util_2::lexcast.
const std::string & my_string_vector_t::operator[]( unsigned int index ) const { if( index >= size() ) throw std::out_of_range( std::string( "index is too big" ) + cpp_util_2::lexcast< std::string >( index ) ); ... } |
или, что еще проще:
// Способ 4. Использование cpp_util_2::slexcast.
const std::string & my_string_vector_t::operator[]( unsigned int index ) const { if( index >= size() ) throw std::out_of_range( std::string( "index is too big" ) + cpp_util_2::slexcast( index ) ); ... } |
То, что для преобразования значений используется промежуточное текстовое представление позволяет не только преобразовывать числовые значения в строку, но и преобразовывать строку в числовое значение.
Вариант функции slexcast() с использованием объекта-преобразователя значения в строку.
void
operator( std::ostream & to, const From & what ) const; |
Вариант функции lexcast() для преобразования в строку.
С помощью cpp_util_2::lexcast() можно попытаться преобразовать любой объект в строку, если у этого объекта определен оператор сдвига в std::ostream. Например:
class A
{ int m_a; float m_b; public : std::ostream & dump_to( std::ostream & to ) const { return ( to << "m_a: " << a << ", m_b: " << m_b ); } }; inline std::ostream & operator<<( std::ostream & to, const A & a ) { return a.dump_to( to ); } void f() { A a; std::string str_a( cpp_util_2::lexcast< std::string >( a ) ); } |
Но проблема в том, что в str_a будет содержаться только "m_a:", т.к. оператор >> для std::string извлекает значения из промежуточного std::stringstream в cpp_util_2::lexcast() только до первого пробела.
Функция slexcast() решает эту проблему и позволяет поместить в str_a все содержимое промежуточного std::stringstream. В приведенном примере функцию slexcast() можно использовать так:
void f()
{ A a; std::string str_a( cpp_util_2::slexcast( a ) ); } |
Упрощенный способ создания объекта const_unary_method_t.
Если есть класс:
class actor_t
{ public : void f( const std::string & a ); }; |
То создать объект unary_method_t для вызова метода f у объекта типа actor_t можно двумя способами:
void call_f_foreach
( actor_t & a, const std::list< std::string > & l ) { std::for_each ( l.begin(), l.end(), cpp_util_2::unary_method_t < void, actor_t, const std::string & > ( &a, &actor_t::f ) ); } |
void call_f_foreach
( actor_t & a, const std::list< std::string > & l ) { std::for_each ( l.begin(), l.end(), cpp_util_2::unary_method( &a, &actor_t::f ) ); } |
unary_method/find_if/main.cpp, unary_method/for_each_find_min_max/main.cpp и unary_method/for_each_modify/main.cpp.
7.2 Пространство имен cpp_util_2::impl
Детали реализации проекта cpp_util_2.
7.3 Пространство имен cpp_util_2::lexcasts
Дополнительные вспомогательные средства для использования вместе с функцией cpp_util_2::lexcast().
Выполнение преобразования всех значений указанного контейнера с использованием указанного преобразователя.
С помощью этой функции можно осуществлять преобразование, например, всех значений контейнера, который обладает методами begin(), end(), возвращающими итераторы:
// Печать шестнадцатиричного представления каждого символа строки.
std::string hello( "Hello!" ); std::cout << cpp_util_2::slexcast( cpp_util_2::lexcasts::all( hello, "", cpp_util_2::lexcasts::hex_bslashx() ) ); |
Выполнение преобразования всех значений между указанными итераторами с использованием указанного преобразователя.
С помощью этой функции можно осуществлять преобразование, например, всех значений контейнера или массива (вспомогательный string_getter используется для того, чтобы прочитать из промежуточного буфера все значения вместе с пробелами):
// Печать всех значений из массива.
unsigned int ui[ 64 ] = { ... }; std::cout << cpp_util_2::slexcast( cpp_util_2::lexcasts::all( ui, ui + ( sizeof(ui) / sizeof(ui[0]) ), " ", cpp_util_2::lexcasts::hex_0x() ) ); |
Выполнение преобразования всех значений указанного контейнера.
С помощью этой функции можно осуществлять преобразование, например, всех значений контейнера, который обладает методами begin(), end(), возвращающими итераторы (вспомогательный string_getter используется для того, чтобы прочитать из промежуточного буфера все значения вместе с пробелами):
// Печать всех значений вектора строк, разделяя каждое значение
// переводом строки. std::set< std::string > ss; ... std::cout << cpp_util_2::slexcast( cpp_util_2::lexcasts::all( ss, "\n" ) ); |
Выполнение преобразования всех значений между указанными итераторами.
С помощью этой функции можно осуществлять преобразование, например, всех значений контейнера или массива (вспомогательный string_getter используется для того, чтобы прочитать из промежуточного буфера все значения вместе с пробелами):
// Печать всех значений из массива.
unsigned int ui[ 64 ] = { ... }; std::cout << cpp_util_2::slexcast( cpp_util_2::lexcasts::all( ui, ui + ( sizeof(ui) / sizeof(ui[0]) ) ) ); // Печать всех значений вектора строк, разделяя каждое значение // переводом строки. std::set< std::string > ss; ... std::cout << cpp_util_2::slexcast( cpp_util_2::lexcasts::all( ss.begin(), ss.end(), "\n" ) ); |
Преобразование значений.
Преобразование значений.
Преобразование значений.
7.4 Пространство имен cpp_util_2::lexcasts::impl
Детали реализации.
#include <util.hpp>
Вспомогательная структура для преобразования значений на которые указывают итераторы.
Объявления и описания членов структуры находятся в файле:
8.2 Шаблон структуры cpp_util_2::lexcasts::all_t< It >
#include <util.hpp>
Вспомогательная структура для преобразования значений на которые указывают итераторы.
Объявления и описания членов структуры находятся в файле:
8.3 Шаблон структуры cpp_util_2::lexcasts::all_using_t< It, Putter >
#include <util.hpp>
Вспомогательная структура для преобразования значений на которые указывают итераторы.
Объявления и описания членов структуры находятся в файле:
8.4 Шаблон класса cpp_util_2::const_unary_method_t< R, T, A >
#include <unary_method.hpp>
Класс-обертка для вызова константного унарного метода объекта.
Подробнее см. cpp_util_2: Обертки для вызова унарных методов объектов.
Инициализирующий конструктор.
Объявления и описания членов класса находятся в файле:
8.5 Структура cpp_util_2::lexcasts::def_putter
#include <util.hpp>
Преобразователь значения, который использует стандартный оператор сдвига, определенный для данного типа.
Объявления и описания членов структуры находятся в файле:
8.6 Структура cpp_util_2::lexcasts::hex
#include <util.hpp>
Преобразование целочисленного значения в шестнадцатиричное представление.
Для того, чтобы получить в строке шестнадцатиричное представление числа можно воспользоваться классом hex_t в качестве объекта-значения для cpp_util_2::lexcast():
unsigned int ui = 2378;
std::string str_hex_ui( cpp_util_2::slexcast( ui, cpp_util_2::lexcasts::hex() ) ); |
Объявления и описания членов структуры находятся в файле:
8.7 Класс cpp_util_2::lexcasts::hex_0x
#include <util.hpp>
Граф наследования:cpp_util_2::lexcasts::hex_0x::
|
Преобразование целочисленного значения в шестнадцатиричное представление с добавлением стандартного для C/C++ префикса 0x.
Для того, чтобы получить в строке шестнадцатиричное представление числа, которое начинается со стандартного C/C++ префикса 0x, можно воспользоваться классом hex_0x в качестве объекта-значения для cpp_util_2::lexcast():
unsigned int ui = 2378;
std::string str_hex_ui( cpp_util_2::slexcast( ui, cpp_util_2::lexcasts::hex_0x() ) ); |
Объявления и описания членов класса находятся в файле:
8.8 Класс cpp_util_2::lexcasts::hex_bslashx
#include <util.hpp>
Граф наследования:cpp_util_2::lexcasts::hex_bslashx::
|
Преобразование целочисленного значения в шестнадцатиричное представление с добавлением стандартного для C/C++ префикса \x.
Для того, чтобы получить в строке шестнадцатиричное представление числа, которое начинается со стандартного C/C++ префикса \x, можно воспользоваться классом hex_bslashx_t в качестве объекта-значения для cpp_util_2::lexcast():
unsigned int ui = 2378;
std::string str_hex_ui( cpp_util_2::slexcast( ui, cpp_util_2::lexcasts::hex_bslashx() ) ); |
Объявления и описания членов класса находятся в файле:
8.9 Структура cpp_util_2::lexcasts::hex_getter
#include <util.hpp>
Преобразование шестнадцатиричное представления числа в число.
Для того, чтобы извлечь из строкого шестнадцатиричное представление числа значение можно воспользоваться классом hex_getter в качестве объекта-извлекателя для cpp_util_2::lexcast():
unsigned int ui = cpp_util_2::lexcast< unsigned int >(
std::string( "ff5d" ), cpp_util_2::lexcasts::def_putter(), cpp_util_2::lexcasts::hex_getter() ); |
Объявления и описания членов структуры находятся в файле:
8.10 Класс cpp_util_2::nocopy_t
#include <nocopy.hpp>
Базовый класс, запрещающий копирование объектов.
Класс, который специально имеет закрытые конструктор и оператор копирования. Благодоря этому производные от nocopy_t классы так же имеют закрытые конструктор и оператор копирования.
Пример использования:
class my_smart_ptr_t : private cpp_util_2::nocopy_t
{ private : // Из-за этого указателя объекты my_smart_ptr_t // не могут копироваться. void * m_ptr; public : my_smart_ptr_t(); ~my_smart_ptr_t(); ... }; |
Благодоря закрытому наследованию нельзя преобразовать my_smart_ptr_t * к cpp_util_2::nocopy_t.
Объявления и описания членов класса находятся в файле:
8.11 Класс cpp_util_2::lexcasts::prefixed_hex
#include <util.hpp>
Граф наследования:cpp_util_2::lexcasts::prefixed_hex::
|
Преобразование целочисленного значения в шестнадцатиричное представление с добавлением указанного префикса.
Объявления и описания членов класса находятся в файле:
8.12 Шаблон класса cpp_util_2::impl::return_type_detector_t< R >
#include <unary_method.hpp>
Обертка для определения типа возвращаемого значения.
Необходимость в этой обертке возникла из-за того, что VC6.0 не позволяет возвращать тип void:
void
f() {} void call_f() { return f(); } |
Поэтому для методов, возвращающих void нужно создавать обертки вызова, которые возврают какой-либо тип, например, int:
void
f() {} int call_f() { f(); return 0; } |
Шаблон return_type_detector_t берет на себя задачу определения типа возвращаемого значения. Для методов, возвращающих void создается специализация return_type_detector_t< void >, которая возвращает тип int.
Тип дополнительного аргумента в для функции call_method.
Функции call_method(), получающие последним аргументом char * предназначены для вызова методов, которые реально что-то возвращают.
Объявления и описания членов класса находятся в файле:
8.13 Шаблон класса cpp_util_2::impl::return_type_detector_t< void >
#include <unary_method.hpp>
Спецализация шаблона return_type_detector_t для методов, возвращающих void.
Тип дополнительного аргумента в для функции call_method.
Функции call_method(), получающие последним аргументом int * предназначены для вызова методов, которые ничего возвращают.
Объявления и описания членов класса находятся в файле:
8.14 Шаблон класса cpp_util_2::unary_method_t< R, T, A >
#include <unary_method.hpp>
Класс-обертка для вызова унарного неконстантного метода объекта.
Подробнее см. cpp_util_2: Обертки для вызова унарных методов объектов.
Инициализирующий конструктор.
Объявления и описания членов класса находятся в файле:
Общий файл для всех макросов.
Подключает файлы с определениями основных макросов.
#include <cpp_util_2/h/defs.hpp>
#include <cpp_util_2/h/detect_compiler.hpp>
9.2 Файл cpp_util_2/h/defs.hpp
Не привязанные к компилятору макросы.
Основные макросы, которые могут использоваться в любых компиляторах.
Возвращает размер массива array в элементах.
Возвращает размерность атрибута attr типа type в байтах.
9.3 Файл cpp_util_2/h/detect_compiler.hpp
Зависящие от компилятора макросы.
Макросы, имена которых для всех компиляторов остаются одинаковыми, но раскрываются по разному.
Основное назначение этих макросов – унификация и скрытие от программиста различных особенностей и/или не стандартизированных расширений C++.
Указание экспортируемости.
Указывает, что класс/функция должна быть экспортируемой.
Декларация экспорта функции.
В различных компиляторах ключевые слова для экспорта функции нужно указывать либо до, либо после указания типа возвращаемого значения.
__declspec(dllexport) void fn() {...}
|
void _export fn() {...}
|
Макрос CPP_UTIL_2_EXPORT_FUNC_SPEC подставляет соответсвующее ключевое слово для экспорта функции либо перед, либо после типа возвращаемого значения.
CPP_UTIL_2_EXPORT_FUNC_SPEC(void) fn() {...}
|
Экспорт класса или структуры.
Аналог CPP_UTIL_2_EXPORT_FUNC_SPEC для типов.
Декларировать extern "C" функцию.
Сформировать extern "C" имя.
Указание импортируемости.
Указывает, что класс/функция должна быть импортируемой.
Декларация импорта функции.
В различных компиляторах ключевые слова для импорта функции нужно указывать либо до, либо после указания типа возвращаемого значения, либо не указывать вовсе.
__declspec(dllimport) void fn() {...}
|
void _import fn() {...}
|
Макрос CPP_UTIL_2_IMPORT_FUNC_SPEC подставляет соответсвующее ключевое слово для экспорта функции либо перед, либо после типа возвращаемого значения.
CPP_UTIL_2_IMPORT_FUNC_SPEC(void) fn() {...}
|
Импорт класса или структуры.
Аналог CPP_UTIL_2_IMPORT_FUNC_SPEC для типов.
9.4 Файл cpp_util_2/h/lexcast.hpp
Функция для конвертации значений из одного типа в другой с использованием промежуточного текстового представления.
#include <sstream>
9.5 Файл cpp_util_2/h/nocopy.hpp
Класс cpp_util_2::nocopy_t.
9.6 Файл cpp_util_2/h/unary_method.hpp
Класс-обертка для вызова унарного метода объекта.
9.7 Файл cpp_util_2/lexcasts/h/util.hpp
Вспомогательные объекты для преобразования и извлечения значений.
#include <cpp_util_2/h/nocopy.hpp>
#include <cpp_util_2/h/lexcast.hpp>
/*
Пример порождения исключения с использованием cpp_util_2::lexcast(). */ #include <map> #include <string> #include <iostream> #include <stdexcept> #include <cpp_util_2/h/lexcast.hpp> // Класс словаря, в котором оператор [] порождает // исключение, если элемент не найден по ключу. template< class Key, class Value, class Pred = std::less< Key > > class hard_map_t : public std::map< Key, Value, Pred > { typedef std::map< Key, Value, Pred > base_type_t; public : hard_map_t() {} hard_map_t( const hard_map_t & o ) : base_type_t( o ) {} hard_map_t & operator=( const hard_map_t & o ) { base_type_t::operator=( o ); return *this; } Value & operator[]( const Key & k ) { typename base_type_t::iterator it = find( k ); if( it == end() ) throw std::invalid_argument( std::string( "Key not found: " ) + cpp_util_2::lexcast< std::string >( k ) ); return it->second; } }; // Проверяем работу карты с целыми ключами. void check_int() { hard_map_t< int, int > m; m.insert( hard_map_t< int, int >::value_type( 0, 0 ) ); m.insert( hard_map_t< int, int >::value_type( -1, -1 ) ); m.insert( hard_map_t< int, int >::value_type( 1, 1 ) ); try { std::cout << "search key: 0 => " << std::flush << m[ 0 ] << std::endl; std::cout << "search key: -1 => " << std::flush << m[ -1 ] << std::endl; std::cout << "search key: 2 => " << std::flush << m[ 2 ] << std::endl; } catch( const std::exception & x ) { std::cout << x.what() << std::endl; } } // Проверяем работу карты со строковыми ключами. void check_string() { hard_map_t< std::string, int > m; m.insert( hard_map_t< std::string, int >::value_type( "A", 0 ) ); m.insert( hard_map_t< std::string, int >::value_type( "B", -1 ) ); m.insert( hard_map_t< std::string, int >::value_type( "C", 1 ) ); try { std::cout << "search key: A => " << std::flush << m[ "A" ] << std::endl; std::cout << "search key: B => " << std::flush << m[ "B" ] << std::endl; std::cout << "search key: D => " << std::flush << m[ "D" ] << std::endl; } catch( const std::exception & x ) { std::cout << x.what() << std::endl; } } int main() { check_int(); check_string(); return 0; } |
/*
Пример работы со средствами из cpp_util_2/lexcast/h/util.hpp. */ #include <map> #include <set> #include <string> #include <iostream> #include <stdexcept> #include <cpp_util_2/lexcasts/h/util.hpp> // Демонстрация работы cpp_util_2::slexcast. void slexcast_demo() { std::string pre( "This is string with spaces." ); std::string post( cpp_util_2::slexcast( pre ) ); std::cout << "*** slexcast_demo:\n\tpre: " << pre << "\n\tpost: " << post << "\n" << std::endl; } // Демонстрация преобразования в шестнадцатиричное представление. void hex_putter_demo() { // Если тип char по умолчанию беззнаковый. char c1 = 16; // Если тип char по умолчанию знаковый. char c2 = static_cast< char >( -3 ); signed char c_negative = -3; unsigned char uc = 16; short s = 16; short s_negative = -3; unsigned short us = 16; int i = 16; int i_negative = -3; unsigned int ui = 16; long l = 16; long l_negative = -3; unsigned long ul = 16; std::cout << "*** hex_putter_demo:\n\t" << (int)c1 << " " << cpp_util_2::slexcast( c1, cpp_util_2::lexcasts::hex() ) << ", " << cpp_util_2::slexcast( c1, cpp_util_2::lexcasts::hex_0x() ) << ", " << cpp_util_2::slexcast( c1, cpp_util_2::lexcasts::hex_bslashx() ) << "\n\t" << (int)c2 << " " << cpp_util_2::slexcast( c2, cpp_util_2::lexcasts::hex() ) << ", " << cpp_util_2::slexcast( c2, cpp_util_2::lexcasts::hex_0x() ) << ", " << cpp_util_2::slexcast( c2, cpp_util_2::lexcasts::hex_bslashx() ) << "\n\t" << (int)c_negative << " " << cpp_util_2::slexcast( c_negative, cpp_util_2::lexcasts::hex() ) << ", " << cpp_util_2::slexcast( c_negative, cpp_util_2::lexcasts::hex_0x() ) << ", " << cpp_util_2::slexcast( c_negative, cpp_util_2::lexcasts::hex_bslashx() ) << "\n\t" << (unsigned int)uc << " " << cpp_util_2::slexcast( uc, cpp_util_2::lexcasts::hex() ) << ", " << cpp_util_2::slexcast( uc, cpp_util_2::lexcasts::hex_0x() ) << ", " << cpp_util_2::slexcast( uc, cpp_util_2::lexcasts::hex_bslashx() ) << "\n\t" << s << " " << cpp_util_2::slexcast( s, cpp_util_2::lexcasts::hex() ) << ", " << cpp_util_2::slexcast( s, cpp_util_2::lexcasts::hex_0x() ) << ", " << cpp_util_2::slexcast( s, cpp_util_2::lexcasts::hex_bslashx() ) << "\n\t" << s_negative << " " << cpp_util_2::slexcast( s_negative, cpp_util_2::lexcasts::hex() ) << ", " << cpp_util_2::slexcast( s_negative, cpp_util_2::lexcasts::hex_0x() ) << ", " << cpp_util_2::slexcast( s_negative, cpp_util_2::lexcasts::hex_bslashx() ) << "\n\t" << us << " " << cpp_util_2::slexcast( us, cpp_util_2::lexcasts::hex() ) << ", " << cpp_util_2::slexcast( us, cpp_util_2::lexcasts::hex_0x() ) << ", " << cpp_util_2::slexcast( us, cpp_util_2::lexcasts::hex_bslashx() ) << "\n\t" << i << " " << cpp_util_2::slexcast( i, cpp_util_2::lexcasts::hex() ) << ", " << cpp_util_2::slexcast( i, cpp_util_2::lexcasts::hex_0x() ) << ", " << cpp_util_2::slexcast( i, cpp_util_2::lexcasts::hex_bslashx() ) << "\n\t" << i_negative << " " << cpp_util_2::slexcast( i_negative, cpp_util_2::lexcasts::hex() ) << ", " << cpp_util_2::slexcast( i_negative, cpp_util_2::lexcasts::hex_0x() ) << ", " << cpp_util_2::slexcast( i_negative, cpp_util_2::lexcasts::hex_bslashx() ) << "\n\t" << ui << " " << cpp_util_2::slexcast( ui, cpp_util_2::lexcasts::hex() ) << ", " << cpp_util_2::slexcast( ui, cpp_util_2::lexcasts::hex_0x() ) << ", " << cpp_util_2::slexcast( ui, cpp_util_2::lexcasts::hex_bslashx() ) << "\n\t" << l << " " << cpp_util_2::slexcast( l, cpp_util_2::lexcasts::hex() ) << ", " << cpp_util_2::slexcast( l, cpp_util_2::lexcasts::hex_0x() ) << ", " << cpp_util_2::slexcast( l, cpp_util_2::lexcasts::hex_bslashx() ) << "\n\t" << l_negative << " " << cpp_util_2::slexcast( l_negative, cpp_util_2::lexcasts::hex() ) << ", " << cpp_util_2::slexcast( l_negative, cpp_util_2::lexcasts::hex_0x() ) << ", " << cpp_util_2::slexcast( l_negative, cpp_util_2::lexcasts::hex_bslashx() ) << "\n\t" << ul << " " << cpp_util_2::slexcast( ul, cpp_util_2::lexcasts::hex() ) << ", " << cpp_util_2::slexcast( ul, cpp_util_2::lexcasts::hex_0x() ) << ", " << cpp_util_2::slexcast( ul, cpp_util_2::lexcasts::hex_bslashx() ) << "\n\t" << 127 << " " << cpp_util_2::slexcast( 127, cpp_util_2::lexcasts::hex() ) << ", " << cpp_util_2::slexcast( 127, cpp_util_2::lexcasts::hex_0x() ) << ", " << cpp_util_2::slexcast( 127, cpp_util_2::lexcasts::hex_bslashx() ) << "\n\t" << -127 << " " << cpp_util_2::slexcast( -127, cpp_util_2::lexcasts::hex() ) << ", " << cpp_util_2::slexcast( -127, cpp_util_2::lexcasts::hex_0x() ) << ", " << cpp_util_2::slexcast( -127, cpp_util_2::lexcasts::hex_bslashx() ) << "\n" << std::endl; } // Демонстрация преобразования из шестнадцатиричного представления в число. void hex_getter_demo() { std::cout << "*** hex_getter_demo:" << std::endl; std::string hex8( "f3" ); char c = cpp_util_2::lexcast< char >( hex8, cpp_util_2::lexcasts::def_putter(), cpp_util_2::lexcasts::hex_getter() ); signed char sc = cpp_util_2::lexcast< signed char >( hex8, cpp_util_2::lexcasts::def_putter(), cpp_util_2::lexcasts::hex_getter() ); unsigned char uc = cpp_util_2::lexcast< unsigned char >( hex8, cpp_util_2::lexcasts::def_putter(), cpp_util_2::lexcasts::hex_getter() ); std::cout << "\n\thex8: " << hex8 << "\n\tchar: " << (short)c << "\n\tsigned char: " << (short)sc << "\n\tunsigned char: " << (unsigned short)uc << std::endl; std::string hex16( "ff5d" ); short s = cpp_util_2::lexcast< short >( hex16, cpp_util_2::lexcasts::def_putter(), cpp_util_2::lexcasts::hex_getter() ); unsigned short us = cpp_util_2::lexcast< unsigned short >( hex16, cpp_util_2::lexcasts::def_putter(), cpp_util_2::lexcasts::hex_getter() ); int i = cpp_util_2::lexcast< int >( hex16, cpp_util_2::lexcasts::def_putter(), cpp_util_2::lexcasts::hex_getter() ); unsigned int ui = cpp_util_2::lexcast< unsigned int >( hex16, cpp_util_2::lexcasts::def_putter(), cpp_util_2::lexcasts::hex_getter() ); std::cout << "\n\thex16: " << hex16 << "\n\tshort: " << s << "\n\tunsigned short: " << us << "\n\tint: " << i << "\n\tunsigned int: " << ui << std::endl; std::string hex32( "ffffff5d" ); int i32 = cpp_util_2::lexcast< int >( hex32, cpp_util_2::lexcasts::def_putter(), cpp_util_2::lexcasts::hex_getter() ); unsigned int ui32 = cpp_util_2::lexcast< unsigned int >( hex32, cpp_util_2::lexcasts::def_putter(), cpp_util_2::lexcasts::hex_getter() ); long l = cpp_util_2::lexcast< long >( hex32, cpp_util_2::lexcasts::def_putter(), cpp_util_2::lexcasts::hex_getter() ); unsigned long ul = cpp_util_2::lexcast< unsigned int >( hex32, cpp_util_2::lexcasts::def_putter(), cpp_util_2::lexcasts::hex_getter() ); std::cout << "\n\thex32: " << hex32 << "\n\tint: " << i32 << "\n\tunsigned int: " << ui32 << "\n\tlong: " << l << "\n\tunsigned long: " << ul << std::endl; } // Преобразование значений контейнера с // использованием cpp_util_2::slexcast(). void container_putter_demo() { std::cout << "*** container_putter_demo:" << std::endl; // Печать всех значений из массива. unsigned int ui[ 8 ] = { 18, 17, 16, 15, 14, 13, 12, 10 }; std::cout << cpp_util_2::slexcast( cpp_util_2::lexcasts::all( ui, ui + ( sizeof(ui) / sizeof(ui[0]) ) ) ) << std::endl; // То же самое, но в шестнадцатиричном виде. std::cout << cpp_util_2::slexcast( cpp_util_2::lexcasts::all( ui, ui + ( sizeof(ui) / sizeof(ui[0]) ), " ", cpp_util_2::lexcasts::hex() ) ) << std::endl; // Печать всех значений вектора строк, разделяя каждое значение // переводом строки. std::set< std::string > ss; ss.insert( "AAA" ); ss.insert( "CCC" ); ss.insert( "BBB" ); ss.insert( "EEE" ); ss.insert( "DDD" ); #if !defined( __BORLANDC__ ) std::cout << cpp_util_2::slexcast( cpp_util_2::lexcasts::all( ss.begin(), ss.end(), "\n" ) ) << std::endl; #endif // Еще раз, но проще. std::cout << cpp_util_2::slexcast( cpp_util_2::lexcasts::all( ss, "\n" ) ) << std::endl; // Отображаем каждый символ строки в шестнадцатиричном виде. std::string hello( "Hello, World" ); std::cout << "ASCII string: " << hello << "\nHex string: " << cpp_util_2::slexcast( cpp_util_2::lexcasts::all( hello.begin(), hello.end(), "", cpp_util_2::lexcasts::hex_bslashx() ) ) // Печатаем тоже самое, но проще. << "\nHex string 2: "<< cpp_util_2::slexcast( cpp_util_2::lexcasts::all( hello, "", cpp_util_2::lexcasts::hex_bslashx() ) ) << std::endl; } int main() { slexcast_demo(); hex_putter_demo(); hex_getter_demo(); container_putter_demo(); return 0; } |
10.3 unary_method/find_if/main.cpp
/*
Пример поиска в списке строк элемента, содержащего указанные две подстроки. */ #include <list> #include <string> #include <algorithm> #include <functional> #include <iostream> #include <cpp_util_2/h/unary_method.hpp> class pred_t { public : pred_t( // Первая искомая строка. const std::string & first, // Вторая искомая строка. const std::string & second ); // Будет вызываться для каждого очередного // элемента списка. // Возвращает true, если обе подстроки найдены. bool next( const std::string & what ) const; private : std::string m_first; std::string m_second; }; pred_t::pred_t( const std::string & first, const std::string & second ) : m_first( first ) , m_second( second ) { } bool pred_t::next( const std::string & what ) const { return ( std::string::npos != what.find( m_first ) && std::string::npos != what.find( m_second ) ); } void search( // Где искать. const std::list< std::string > & where, // Что искать. const std::string & first, const std::string & second ) { std::cout << "search (" << first << "," << second << "): "; // Ищем элемент. pred_t pred( first, second ); std::list< std::string >::const_iterator it( std::find_if( where.begin(), where.end(), cpp_util_2::unary_method( // У этого объекта... &pred, // ... будет вызван метод. &pred_t::next ) ) ); if( it != where.end() ) std::cout << "found: " << *it << std::endl; else std::cout << "not found" << std::endl; } int main() { std::list< std::string > l; l.push_back( "4444 3333 2222 777" ); l.push_back( "333 7777" ); l.push_back( "666 444 111 666" ); l.push_back( "7777777" ); l.push_back( "22 333 777 8888 444" ); l.push_back( "55555" ); search( l, "333", "888" ); search( l, "333", "111" ); return 0; } |
10.4 unary_method/for_each_find_min_max/main.cpp
/*
Пример поиска в списке строк с минимальной и максимальной длиной. */ #include <list> #include <string> #include <algorithm> #include <iostream> #include <cpp_util_2/h/unary_method.hpp> class finder_t { public : // Будет вызываться для каждого очередного // элемента списка. void next( const std::string & what ); const std::string & min() const; const std::string & max() const; private : std::string m_min; std::string m_max; }; void finder_t::next( const std::string & what ) { // Считаем, что в списке нет пустых строк, // поэтому пустой m_min может быть только // в начале поиска. if( m_min.empty() || m_min.length() > what.length() ) { m_min = what; } if( m_max.length() < what.length() ) m_max = what; } const std::string & finder_t::min() const { return m_min; } const std::string & finder_t::max() const { return m_max; } // Найти самую короткую и самую длинную строки. void find( const std::list< std::string > & where, std::string & min, std::string & max ) { finder_t finder; std::for_each( where.begin(), where.end(), cpp_util_2::unary_method( // У этого объекта... &finder, // ... будет вызываться метод next. &finder_t::next ) ); min = finder.min(); max = finder.max(); } int main() { std::list< std::string > l; l.push_back( "4444" ); l.push_back( "333" ); l.push_back( "666666" ); l.push_back( "7777777" ); l.push_back( "22" ); l.push_back( "55555" ); std::string min, max; find( l, min, max ); std::cout << "min: " << min << ", max: " << max << std::endl; return 0; } |
10.5 unary_method/for_each_modify/main.cpp
/*
Пример изменения каждого элемента списка строк. */ #include <list> #include <string> #include <algorithm> #include <functional> #include <iostream> #include <cpp_util_2/h/unary_method.hpp> class modificator_t { public : modificator_t( // Добавляемый к строке префикс. const std::string & prefix, // Добавляемый к строке суффикс. const std::string & suffix ); // Будет вызываться для каждого очередного // элемента списка. void next( std::string & what ) const; private : std::string m_prefix; std::string m_suffix; }; modificator_t::modificator_t( const std::string & prefix, const std::string & suffix ) : m_prefix( prefix ) , m_suffix( suffix ) { } void modificator_t::next( std::string & what ) const { what.insert( 0, m_prefix ); what += m_suffix; } // Печатает очередной элемент списка. void printer( const std::string & a ) { std::cout << a << " "; } int main() { std::list< std::string > l; l.push_back( "4444" ); l.push_back( "333" ); l.push_back( "666666" ); l.push_back( "7777777" ); l.push_back( "22" ); l.push_back( "55555" ); // Печатаем исходный список. std::for_each( l.begin(), l.end(), printer ); std::cout << std::endl; // Модифицируем все элементы. modificator_t m( "-=: ", " :=-" ); std::for_each( l.begin(), l.end(), cpp_util_2::unary_method( // У этого объекта... &m, // ... будет вызван метод. &modificator_t::next ) ); // Печатаем полученный список. std::for_each( l.begin(), l.end(), printer ); std::cout << std::endl; return 0; } |
Необходимость в классах cpp_util_2::unary_method_t (cpp_util_2::const_unary_method_t) возникла при очередном использовании функции std::for_each. Был список std::string и был класс у которого нужно было вызвать метод для каждого из элементов список. Простое решение:
class actor_t
{ public : void some_method( const std::string & o ); ... }; void initialize_actor ( actor_t & actor, const std::list< std::string > & l ) { for( std::list< std::string >::const_iterator it = l.begin(), it_end = l.end(); it != it_end; ++it ) { actor.some_method( *it ); } } |
хотелось заменить на использование std::for_each:
void
initialize_actor ( actor_t & actor, const std::list< std::string > & l ) { std::for_each( l.begin(), l.end(), ... ); } |
С помощью STL это можно было бы сделать, например, так:
void
initialize_actor ( actor_t & actor, const std::list< std::string > & l ) { std::for_each ( l.begin(), l.end(), std::bind1st ( std::mem_fun( &actor_t::some_method ), &actor ) ); } |
Но здесь возникают сразу две проблемы:
Проблему с построением ссылки на ссылку можно попробовать обойти с помощью boost::bind1st:
void
initialize_actor ( actor_t & actor, const std::list< std::string > & l ) { std::for_each ( l.begin(), l.end(), boost::bind1st ( std::mem_fun( &actor_t::some_method ), &actor ) ); } |
Но и здесь нашлись подводные камни: компилятор Visual C++ выдавал следующую диагностику:
test\unary_method\boost_way\for_each.cpp(64) : error C2782: ’boost::binder1st<Op
eration> boost::bind1st(Operation &,boost::call_traits<Operation>::param_type)’ : template parameter ’Operation’ is ambiguous could be ’actor_t *’ or ’std::const_mem_fun1_t<_Result,_Ty,_Arg>’ with [ _Result=void, _Ty=actor_t, _Arg=const std::basic_string<char,std::char_traits<char>,std::alloca tor<char>> & ] |
Хотя другие компиляторы (Borland C++ 5.6, MinGW 3.0.0) успешно компилировали данный код. Visual C++ 6.0 так же выдавал ошибки, но свои:
test\unary_method\boost_way\for_each.cpp(64) : error C2784: ’class std::mem_fun_
t<_R,_Ty> __cdecl std::mem_fun(_R (__thiscall _Ty::*)(void))’ : could not deduce template argument for ’<Unknown>’ from ’void (__thiscall actor_t::*)(const clas s std::basic_string<char,struct std::char_traits<char>,class std::allocator<char > > &) const’ |
А основная проблема была в том, что нужно было использовать как раз Visual C++ версий 6.0 и 7.0.
Кроме того, конструкция:
boost::bind1st
( std::mem_fun( &actor_t::some_method ), &actor ) |
для вызова метода объекта выглядит не сильно понятной с первого взгляда.
Из-за всего вышеизложенного были созданы классы cpp_util_2::unary_method_t и cpp_util_2::const_unary_method_t, которые позволили решить эту же задачу под всеми имеющимися у меня компиляторами:
void
initialize_actor ( actor_t & actor, const std::list< std::string > & l ) { std::for_each ( l.begin(), l.end(), cpp_util_2::unary_method ( &actor, &actor_t::some_method ) ); } |
По сравнению с реализацией boost::bind1st классы cpp_util_2::unary_method_t и cpp_util_2::const_unary_method_t менее эфективны, т.к. при вызове метода объекта используется вспомогательная функция, которая получает дополнительный аргумент-указатель. Этот аргумент нигде не используется, поэтому хороший оптимизирующий компилятор, наверное, сможет простроить не менее эффективный код по сравнению с boost::bind1st. Но если оптимизатор не сможет игнорировать использование этого параметра, то реализация unary_method_t должна проигрывать реализации boost::bind1st. Но на современной технике и на очень больших количествах вызовах этот проигрыш вряд ли составит даже миллисекунды.
Идея создания средств cpp_util_2::lexcasts появилась после знакомства с проектом boost::lexical_cast. Реализация lexical_cast в boost не устраивала меня по нескольким причинам:
Между тем, сама идея средств boost::lexical_cast показалась мне очень интересной и полезной. Поэтому я решил реализовать подобные средства с учетом собственных идей и предпочтений в проекте cpp_util_2.
Средства cpp_util_2::lexcasts предназначены для удобного преобразования различных значений в строку или из строки. Например, при обработке ошибок часто возникает задача преобразовать в строку одно целочисленное значение. Причем в десятичном и шестнадцатиричном виде:
void some_func( unsigned int param )
{ ... if( some_error ) { char sz[ 48 ]; sprintf( sz, "%u (0x%x)", param, param ); throw std::invalid_argument( std::string( "Invalid param: " ) + sz ); } } |
Либо, аналогичный фрагмент можно было бы переписать с использованием std::ostringstream:
void some_func( unsigned int param )
{ ... if( some_error ) { std::ostringstream errs; errs << param << std::hex << param; throw std::invalid_argument( std::string( "Invalid param: " ) + errs.str() ); } } |
Это уже лучше, чем с использованием sprintf, но все равно не удобно – вместо одного оператора throw приходится писать три оператора.
Применение cpp_util_2::slexcast() позволяет использовать только один оператор throw:
void some_func( unsigned int param )
{ ... if( some_error ) throw std::invalid_argument( std::string( "Invalid param: " ) + cpp_util_2::slexcast( param ) + " " + cpp_util_2::slexcast( param, cpp_util_2::lexcasts::hex_0x() ) ); } |
Принцип работы функций cpp_util_2::lexcast(), cpp_util_2::slexcast() очень простой: создается объект std::stringstream в который сначала "сдвигается" (<<) исходное значение, а затем "выдвигается" (>>) результирующее значение. Т.е. вызов cpp_util_2::lexcast<B>(a) можно представить в виде:
std::stringstream ss;
ss << a; ss.seekg( 0 ); B b; ss >> b; |
В случае использования Putter-ов и Getter-ов сдвиги в/из std::stringstream осуществляют Putter-ы и/или Getter-ы. Т.е. для случая cpp_util_2::lexcast<B>(a, p, g):
std::stringstream ss;
p( ss, a ); ss.seekg( 0 ); B b; g( ss, b ); |
Если для структуры или класса определен оператор сдвига в std::ostream, то cpp_util_2::lexcast() позволяет преобразовать объект этого класса в строку. Например:
struct my_struct_t
{ int m_a; std::string m_b; float m_c; }; std::ostream & operator<<( std::ostream & to, const my_struct_t & o ) { return ( to << "my_struct_t{ m_a: " << o.m_a << ", m_b: " << o.m_b << ", m_c: " << o.m_c << "}" ); } |
Но здесь есть подводный камень: стандартный operator>>(std::istream &, std::string &) считывает значение в строку до первого пробела. Поэтому выполнение:
my_struct_t a;
std::string b( cpp_util_2::lexcast< std::string >( a ) ); |
приведет к тому, что b будет равно "my_struct_t{".
Для того, чтобы результирующая строка содержала все представление объекта my_struct_t необходимо использовать функцию cpp_util_2::slexcast(). Эта функция извлекает из промежуточного std::stringstream все содержимое не обращаясь к operator>>(std::istream &, std::string &).
Иногда желательно преобразовать в строку содержимое какого-то контейнера. Для этих целей предназначены функции cpp_util_2::lexcats::all(). Например, если требуется отобразить все элементы std::list< int >:
std::list< int > l;
... std::string result( cpp_util_2::slexcast( cpp_util_2::lexcasts::all( l, " " ) ) ); |
В этом случае в строку помещаются все значения, находящиеся в l, каждое значение разделяется пробелом. После последнего значения пробел не печается.
В функцию cpp_util_2::lexcasts::all() можно передать объект Putter. Например, для того, чтобы отобразить все значения из l в шестнадцатиричном виде можно написать:
std::string result( cpp_util_2::slexcast(
cpp_util_2::lexcasts::all( l, " ", cpp_util_2::lexcasts::hex() ) ) ); |
Иногда возникает необходимость отобразить все содержимое std::string в виде шестнадцатиричного представления каждого из символов в C-формате (т.е. с использованием префикса \x). Для этого можно воспользоваться конструкцией:
std::string a( ... );
std::string result( cpp_util_2::slexcast( cpp_util_2::lexcasts::all( a, "" /* Это не пробел, а пустая строка! */, cpp_util_2::lexcasts::hex_bslashx() ) ) ); |
С функции cpp_util_2::lexcasts::all() можно использовать итераторы. Например, для того, чтобы отобразить все элементы массива:
int array[ 10 ] = { ... };
std::cout << cpp_util_2::slexcast( cpp_util_2::lexcasts::all( array, array + ( sizeof( array ) / sizeof( array[ 0 ] ) ), " " ) ); |
Или все элементы множества, большие указанного значения:
std::set< int > s;
... std::cout << cpp_util_2::slexcast( cpp_util_2::lexcasts::all( s.upper_bound( k ), s.end(), "\n" ) ); |
Иногда требуется создать собственный объект Putter. Например в следующих случаях:
1. Класс, который требуется преобразовать определен в чужом namespace. И в этом namespace для класса не определен оператор сдвига в std::ostream. Т.е.
namespace ns1
{ class useful_t { ... }; } namespace ns2 { void process( const ns1::useful_t & o ) { // ОШИБКА: не будет скомпилировано! std::string s( cpp_util_2::slexcast( o ) ); } } |
Решением может быть:
namespace ns2
{ struct useful_putter { void operator()( std::ostream & to, const ns1::useful_t & o ) { ... } }; void process( const ns1::useful_t & o ) { std::string s( cpp_util_2::slexcast( o, useful_putter() ) ); } } |
Либо:
namespace ns2
{ void useful_putter( std::ostream & to, const ns1::useful_t & o ) { ... } void process( const ns1::useful_t & o ) { std::string s( cpp_util_2::slexcast( o, useful_putter ) ); } } |
2. Необходимо отображать не весь объект, а только его часть. Например, если необходимо отобразить только значение элемента std::map, но не ключ этого элемента. Решением может быть:
typedef std::map< int, std::string > str_by_int_map_t;
void value_putter( std::ostream & to, const str_by_int_map_t::value_type & o ) { to << o.second; } // Отображение всех значений всех элементов, чей ключ больше указанного. void show_values_for_greater_keys( int k, const str_by_int_map_t & m ) { std::cout << cpp_util_2::slexcast( cpp_util_2::lexcasts::all( m.upper_bound( k ), m.end(), "\n", value_putter ) ) << std::endl; } |
2004.02.18
Преобразование и упрощение функций cpp_util_2::lexcast(), cpp_util_2::slexcast().
Преобразование и упрощение средств из пространства имен cpp_util_2::lexcasts:
Добавлен класс cpp_util_2::lexcasts::def_putter.
2004.01.11, 2004.01.14
Добавлены функции cpp_util_2::slexcast().
Добавлены функции cpp_util_2::lexcasts::all(), упрощающие преобразование векторов и контейнеров.
Добавлены классы cpp_util_2::lexcasts::hex_t, cpp_util_2::lexcasts::prefixed_hex_t и функции cpp_util_2::lexcasts::hex_0x(), cpp_util_2::lexcasts::hex_bslashx(), которые упрощают преобразование значений в шестнадцатиричное представление.
Добавлены классы cpp_util_2::putter_t, cpp_util_2::getter_t и несколько перегруженных вариантов функции cpp_util_2::lexcast().
Добавлено пространство имен cpp_util_2::lexcasts.
Добавлен класс cpp_util_2::lexcasts::string_getter_t и функция cpp_util_2::lexcasts::string_getter().
2003.12.27
Добавлена функция cpp_util_2::lexcast().
2003.11.20
Добавлены классы cpp_util_2::unary_method_t, cpp_util_2::const_unary_method_t.