Well, as you've probably already noticed, I've started removing dependencies on the so-called "unsafe" string library functions from all my open-source libraries.
As part of this continuing (and somewhat life-sapping) activity, I'm moving through the STLSoft libraries. The latest release incorporates changes to the important winstl::basic_findfile_sequence class template, which is an STL extension over the Windows FindFile API.
This makes use of a new facility that's been added to the winstl::system_traits class templates: char_copy(). This (static) function is effectively a typed memcpy().
class winstl::system_traits<char>
{
. . .
public:
char* char_copy(char* dest, char const* src, size_t n);
}
class winstl::system_traits<wchar_t>
{
. . .
public:
wchar_t* char_copy(wchar_t* dest, wchar_t const* src, size_t n);
}
It copies exactly the number of characters specified, with no checks on the actual length of src.
The modest benefits are that it is a bit type-safe, and that you don't have to keep having to remember sizeof(char_type) * n.
I'm not totally struck on the name, and did consider borrowing other names. I liked BSD's bcopy() (to mean block copy), but it's too likely that some code somewhere #defines it to memcpy().
I'll gradually filter char_copy() throughout the rest of WinSTL, and then through UNIXSTL, as I work my way through the thankless task of getting "unsafe"-free. Ho hum.
Musings of an STL-extension library designer (who's renowned for producing high-quality software with low-quality documentation).
Saturday, January 24, 2009
Wednesday, January 21, 2009
winstl::squeeze_path() redux
As of 1.9.69, the winstl::squeeze_path() function template is much improved, mainly due to extensive unit-testing. This, in turn, is a fall-out of the the removal of dependencies on so-called "unsafe" string functions, such as strncpy(), strcat(), and so forth. And that, in turn, is due to a "need" to be compatible with Microsoft's perverse designation of standard library functions as deprecated. Ho hum.
Anyway, the improvements now mean that it's robust for all buffer sizes between [0, strlen(path)] and beyond. So, you can do all of the following:
std::string path = "H:\\xyz\\mno\\abcdef.ghi";
char buffer[101];
// returns the number required (22)
winstl::path_squeeze(path, static_cast(NULL), 0);
// returns 1; buffer == ""
winstl::path_squeeze(path, buffer, 1);
// returns 2; buffer == "a"
winstl::path_squeeze(path, buffer, 2);
// returns 5; buffer == "abcd"
winstl::path_squeeze(path, buffer, 5);
// returns 6; buffer == "a...i"
winstl::path_squeeze(path, buffer, 6);
// returns 9; buffer == "ab...ghi"
winstl::path_squeeze(path, buffer, 9);
// returns 10; buffer == "abc...ghi"
winstl::path_squeeze(path, buffer, 10);
// returns 11; buffer == "abcdef.ghi"
winstl::path_squeeze(path, buffer, 11);
// returns 11; buffer == "abcdef.ghi"
winstl::path_squeeze(path, buffer, 12);
// returns 11; buffer == "abcdef.ghi"
winstl::path_squeeze(path, buffer, 17);
// returns 18; buffer == "H:\\...\\abcdef.ghi"
winstl::path_squeeze(path, buffer, 18);
// returns 19; buffer == "H:\\x...\\abcdef.ghi"
winstl::path_squeeze(path, buffer, 19);
// returns 20; buffer == "H:\\xy...\\abcdef.ghi"
winstl::path_squeeze(path, buffer, 20);
// returns 21; buffer == "H:\\xyz...\\abcdef.ghi"
winstl::path_squeeze(path, buffer, 21);
// returns 22; buffer == "H:\\xyz\\mno\\abcdef.ghi"
winstl::path_squeeze(path, buffer, 22);
// returns 22; buffer == "H:\\xyz\\mno\\abcdef.ghi"
winstl::path_squeeze(path, buffer, 23);
Anyway, the improvements now mean that it's robust for all buffer sizes between [0, strlen(path)] and beyond. So, you can do all of the following:
std::string path = "H:\\xyz\\mno\\abcdef.ghi";
char buffer[101];
// returns the number required (22)
winstl::path_squeeze(path, static_cast
// returns 1; buffer == ""
winstl::path_squeeze(path, buffer, 1);
// returns 2; buffer == "a"
winstl::path_squeeze(path, buffer, 2);
// returns 5; buffer == "abcd"
winstl::path_squeeze(path, buffer, 5);
// returns 6; buffer == "a...i"
winstl::path_squeeze(path, buffer, 6);
// returns 9; buffer == "ab...ghi"
winstl::path_squeeze(path, buffer, 9);
// returns 10; buffer == "abc...ghi"
winstl::path_squeeze(path, buffer, 10);
// returns 11; buffer == "abcdef.ghi"
winstl::path_squeeze(path, buffer, 11);
// returns 11; buffer == "abcdef.ghi"
winstl::path_squeeze(path, buffer, 12);
// returns 11; buffer == "abcdef.ghi"
winstl::path_squeeze(path, buffer, 17);
// returns 18; buffer == "H:\\...\\abcdef.ghi"
winstl::path_squeeze(path, buffer, 18);
// returns 19; buffer == "H:\\x...\\abcdef.ghi"
winstl::path_squeeze(path, buffer, 19);
// returns 20; buffer == "H:\\xy...\\abcdef.ghi"
winstl::path_squeeze(path, buffer, 20);
// returns 21; buffer == "H:\\xyz...\\abcdef.ghi"
winstl::path_squeeze(path, buffer, 21);
// returns 22; buffer == "H:\\xyz\\mno\\abcdef.ghi"
winstl::path_squeeze(path, buffer, 22);
// returns 22; buffer == "H:\\xyz\\mno\\abcdef.ghi"
winstl::path_squeeze(path, buffer, 23);
Friday, January 2, 2009
Working with other libraries, part 3: use consistent conventions for member types
Ever get confused by the names of member variables/types/functions of C++ class templates?
Boost.Format's basic_format class template defines five public and two private member types, using a mix of four different naming conventions!
[an extract from boost/format/format_class.hpp]
// in namespace boost
template <class Ch, class Tr, class Alloc>
class basic_format
{
private:
typedef typename io::CompatTraits<Tr>::compatible_type compat_traits;
typedef io::detail::stream_format_state<Ch, Tr> stream_format_state;
public:
typedef Ch CharT;
typedef std::basic_string<Ch, Tr, Alloc> string_type;
typedef typename string_type::size_type size_type;
typedef io::detail::format_item<Ch, Tr, Alloc> format_item_t;
typedef io::basic_altstringbuf<Ch, Tr, Alloc> internal_streambuf_t;
In Extended STL, volume 1: Collections and Iterators I recommend the use of the following naming convention for member types:
I've been using this for many years without a blip. I can read my code, even some years later, and understand what's a type and what isn't and also, importantly, which types are for consumption in the outside world and which are internal to the component. Q.E.D.
Boost.Format's basic_format class template defines five public and two private member types, using a mix of four different naming conventions!
[an extract from boost/format/format_class.hpp]
// in namespace boost
template <class Ch, class Tr, class Alloc>
class basic_format
{
private:
typedef typename io::CompatTraits<Tr>::compatible_type compat_traits;
typedef io::detail::stream_format_state<Ch, Tr> stream_format_state;
public:
typedef Ch CharT;
typedef std::basic_string<Ch, Tr, Alloc> string_type;
typedef typename string_type::size_type size_type;
typedef io::detail::format_item<Ch, Tr, Alloc> format_item_t;
typedef io::basic_altstringbuf<Ch, Tr, Alloc> internal_streambuf_t;
In Extended STL, volume 1: Collections and Iterators I recommend the use of the following naming convention for member types:
- For public member types that share names with standard components that have the same logical purpose, follow the standard convention and use the standard name. An example would be iterator.
- For public member types that are not covered by clause 1, use the _type suffix. An example would be char_type.
- For private member types, use the _type_ suffix. An example would be compat_traits_type_.
- For public API (non-member) types use the _t suffix. An example would be pan_char_t.
- For non-public/implementation API (non-member) types use the _t_ suffix. An example would be b64ErrorString_t_.
I've been using this for many years without a blip. I can read my code, even some years later, and understand what's a type and what isn't and also, importantly, which types are for consumption in the outside world and which are internal to the component. Q.E.D.
Thursday, January 1, 2009
Working with other libraries, part 2: allocators
Much is often made of the supposedly still-born Allocator concept in the standard library. However, one very good use of them is in tracking memory.
At the moment I'm preparing some analyses of FastFormat's performance for an article I'm writing. One of the analyses conducting is to see how many memory allocations are involved in a formatting statement, for each of the comparison libraries. The standard way to achieve something like this is to overload the global operators new, as in:
// NOTE: this code is only valid for single-threaded operation
extern int s_nallocs = 0;
#ifdef OVERLOAD_OPNEW
void* counting_malloc(size_t cb)
{
++s_nallocs;
return ::malloc(cb);
}
void counting_free(void* pv)
{
::free(pv);
}
void* operator new(size_t cb)
{
return counting_malloc(cb);
}
void operator delete(void* pv)
{
counting_free(pv);
}
void* operator new[](size_t cb)
{
return counting_malloc(cb);
}
void operator delete[](void* pv)
{
counting_free(pv);
}
#endif /* OVERLOAD_OPNEW */
Unfortunately, some components with some compilers - the exact permutations escape me at this point - don't go through operator new. This might be because they use a per-class operator new, or it might be because they use an allocator that doesn't use new. (Being under a publishing deadline, I didn't have the time - nor the inclination, if I'm honest - to find out which it was in each case.)
So, in order to get a fighting chance at an accurate depiction of how much memory each library is using I decided to force the issue, by requiring all the strings used to be an instance of the following specialisation, rather than std::string:
typedef std::basic_string<
char
, std::char_traits<char>
, stlsoft::new_allocator<char>
> string_t;
Of course, things aren't ever that simple. Such a string type is not compatible with the IOStreams default specialisations, requiring:
typedef std::basic_stringstream<
char
, std::char_traits<char>
, stlsoft::new_allocator<char>
> stringstream_t;
And the same thing applies for Boost.Format, requiring:
typedef boost::basic_format<
char
, std::char_traits<char>
, stlsoft::new_allocator<char>
> format_t;
Unfortunately, Loki's SafeFormat library does not allow for the specification of allocators (or character traits, for that matter), and only uses std::string. So a little horrifying trickery was required.
Step 1: Introduce string_t into the std namespace.
namespace std
{
using ::string_t;
}
Now, if you've been paying attention this last decade or so you'll know that adding to the std namespace is strictly controlled. I won't go over the rules now; you can look it up. Suffice to say that this action is not allowed.
Of course, needs must, and in this case there's no choice. Since it's just a perf-test program, it's ok. Just don't go using this tactic in production code.
Step 2: Make Loki (and any other code, for that matter) think that std::string_t is std::string.
#define string string_t
I warned you it was horrid!
Step 3: #include the Loki.SafeFormat header
#include<loki/safeformat.h>
Obviously, this has to be done after steps 1 & 2, otherwise it won't work.
There were a few other dodgy things I had to do to get it to work with some really stupid compilers, but that'll have to wait until another day.
At the moment I'm preparing some analyses of FastFormat's performance for an article I'm writing. One of the analyses conducting is to see how many memory allocations are involved in a formatting statement, for each of the comparison libraries. The standard way to achieve something like this is to overload the global operators new, as in:
// NOTE: this code is only valid for single-threaded operation
extern int s_nallocs = 0;
#ifdef OVERLOAD_OPNEW
void* counting_malloc(size_t cb)
{
++s_nallocs;
return ::malloc(cb);
}
void counting_free(void* pv)
{
::free(pv);
}
void* operator new(size_t cb)
{
return counting_malloc(cb);
}
void operator delete(void* pv)
{
counting_free(pv);
}
void* operator new[](size_t cb)
{
return counting_malloc(cb);
}
void operator delete[](void* pv)
{
counting_free(pv);
}
#endif /* OVERLOAD_OPNEW */
Unfortunately, some components with some compilers - the exact permutations escape me at this point - don't go through operator new. This might be because they use a per-class operator new, or it might be because they use an allocator that doesn't use new. (Being under a publishing deadline, I didn't have the time - nor the inclination, if I'm honest - to find out which it was in each case.)
So, in order to get a fighting chance at an accurate depiction of how much memory each library is using I decided to force the issue, by requiring all the strings used to be an instance of the following specialisation, rather than std::string:
typedef std::basic_string<
char
, std::char_traits<char>
, stlsoft::new_allocator<char>
> string_t;
Of course, things aren't ever that simple. Such a string type is not compatible with the IOStreams default specialisations, requiring:
typedef std::basic_stringstream<
char
, std::char_traits<char>
, stlsoft::new_allocator<char>
> stringstream_t;
And the same thing applies for Boost.Format, requiring:
typedef boost::basic_format<
char
, std::char_traits<char>
, stlsoft::new_allocator<char>
> format_t;
Unfortunately, Loki's SafeFormat library does not allow for the specification of allocators (or character traits, for that matter), and only uses std::string. So a little horrifying trickery was required.
Step 1: Introduce string_t into the std namespace.
namespace std
{
using ::string_t;
}
Now, if you've been paying attention this last decade or so you'll know that adding to the std namespace is strictly controlled. I won't go over the rules now; you can look it up. Suffice to say that this action is not allowed.
Of course, needs must, and in this case there's no choice. Since it's just a perf-test program, it's ok. Just don't go using this tactic in production code.
Step 2: Make Loki (and any other code, for that matter) think that std::string_t is std::string.
#define string string_t
I warned you it was horrid!
Step 3: #include the Loki.SafeFormat header
#include
Obviously, this has to be done after steps 1 & 2, otherwise it won't work.
There were a few other dodgy things I had to do to get it to work with some really stupid compilers, but that'll have to wait until another day.
Tuesday, December 30, 2008
Working with other libraries, part 1: Loki performance testing in FastFormat
FastFormat's performance test programs are available from the command-line, via "make -e test.performance".
Since 0.2.1 alpha 2, the distribution has included support for testing against Boost's Boost.Format library. As long as you have an environment variable BOOST defined - under which the boost directory resides - then the above make command-line will provide you with performance tests including Boost.Format. That's reasonably easy to do, because Boost.Format is header-only.
With 0.2.1 beta 6, I needed to include Loki's SafeFormat library. This presented something of a challenge, however, because Loki comes as header+implementation, i.e. it has .cpp files.
FastFormat, like several other STLSoft-related, libraries builds an exhaustive set of configurations. For example, with Visual C++ 9, it builds debug multithreaded, debug multithreaded dll, release multithreaded, and release multithreaded dll. Loki's distribution includes VC++ project/solution files that only provide the multithreaded (not the multithreaded dll) configurations. Problem: how to use Loki.SafeFormat if Loki is detected (by presence of LOKI environment variable).
This problem had me somewhat vexed, so I did what I usually did when I can't think: I went for a ride.
Upon my return, I hit upon the solution: build a specific FastFormat-specific library containing only the source file for the SafeFormat part of Loki. If the makefile detects the definition of the LOKI variable, it will attempt to build the lib/loki.safefmt.ff.??? library (where ??? refers to the per-compiler/architecture/mode configuration).
If that library does not exist, it is linked from a corresponding object, which itself is built from a file src/loki.safefmt/loki.safefmt.cpp. If this file does not exist, it is automatically generated. This file contains exactly one (pre-processor) statement:
#include <../src/SafeFormat.cpp>
which includes the requisite file form the Loki source directory.
The reason this is done is to simplify the situation if the Loki source cannot be found. As you will be aware, failing to find a required file in a makefile-build can lead to less-then-clear error messages. By generating the file if it's not there, and by #include-ing the requisite Loki source file (SafeFormat.cpp), it'll be clearer to users if that file is missing, or in the wrong directory. It also seems preferable to distributing a Loki-related source file in the FastFormat distribution, merely to allow for comparative performance testing.
So, whether you use Loki or not, you can build and test FastFormat without needing any dependencies on it. All you need to include Loki.SafeFormat in the tests is to define the LOKI variable, in the same way as if you wish to include Boost.Format in the tests you define the BOOST variable.
Since 0.2.1 alpha 2, the distribution has included support for testing against Boost's Boost.Format library. As long as you have an environment variable BOOST defined - under which the boost directory resides - then the above make command-line will provide you with performance tests including Boost.Format. That's reasonably easy to do, because Boost.Format is header-only.
With 0.2.1 beta 6, I needed to include Loki's SafeFormat library. This presented something of a challenge, however, because Loki comes as header+implementation, i.e. it has .cpp files.
FastFormat, like several other STLSoft-related, libraries builds an exhaustive set of configurations. For example, with Visual C++ 9, it builds debug multithreaded, debug multithreaded dll, release multithreaded, and release multithreaded dll. Loki's distribution includes VC++ project/solution files that only provide the multithreaded (not the multithreaded dll) configurations. Problem: how to use Loki.SafeFormat if Loki is detected (by presence of LOKI environment variable).
This problem had me somewhat vexed, so I did what I usually did when I can't think: I went for a ride.
Upon my return, I hit upon the solution: build a specific FastFormat-specific library containing only the source file for the SafeFormat part of Loki. If the makefile detects the definition of the LOKI variable, it will attempt to build the lib/loki.safefmt.ff.??? library (where ??? refers to the per-compiler/architecture/mode configuration).
If that library does not exist, it is linked from a corresponding object, which itself is built from a file src/loki.safefmt/loki.safefmt.cpp. If this file does not exist, it is automatically generated. This file contains exactly one (pre-processor) statement:
#include <../src/SafeFormat.cpp>
which includes the requisite file form the Loki source directory.
The reason this is done is to simplify the situation if the Loki source cannot be found. As you will be aware, failing to find a required file in a makefile-build can lead to less-then-clear error messages. By generating the file if it's not there, and by #include-ing the requisite Loki source file (SafeFormat.cpp), it'll be clearer to users if that file is missing, or in the wrong directory. It also seems preferable to distributing a Loki-related source file in the FastFormat distribution, merely to allow for comparative performance testing.
So, whether you use Loki or not, you can build and test FastFormat without needing any dependencies on it. All you need to include Loki.SafeFormat in the tests is to define the LOKI variable, in the same way as if you wish to include Boost.Format in the tests you define the BOOST variable.
Monday, December 29, 2008
Width/Alignment/Fill functionality for FastFormat (0.3) functionally complete
Work on FastFormat has been progressing substantially over the last few days, in preparation for:
1. Output a string that uses a single argument twice, with default formatting in each case:
ff::fmtln(std::cout, "x={0}; y={0};", "abc");
This prints x=abc; y=abc;
2. Specify a minimum width for the first parameter:
ff::fmtln(std::cout, "x={0}; y={0,10};", "abc");
This prints x=abc; y= abc;
3. Specify a minimum width and right alignment (which is the default) for the first parameter:
ff::fmtln(std::cout, "x={0}; y={0,10,,>};", "abc");
This prints x=abc; y= abc;
4. Specify a minimum width and left alignment for the first parameter:
ff::fmtln(std::cout, "x={0}; y={0,10,,<};", "abc");
This prints x=abc; y=abc ;
5. Specify a minimum width and centre alignment for the first parameter:
ff::fmtln(std::cout, "x={0}; y={0,10,,^};", "abc");
This prints x=abc; y= abc ;
6. Specify a maximum width for the second parameter:
ff::fmtln(std::cout, "x={0}; y={0,,2};", "abc");
This prints x=abc; y=bc;
7. Specify a maximum width and left alignment for the second parameter:
ff::fmtln(std::cout, "x={0}; y={0,,2,<};", "abc");
This prints x=abc; y=ab;
The precise semantics of the alignment and fill (not shown above) options may still be changed, but the basis of the parameter syntax (four fields: index, min-width, max-width, alignment-and-fill) seem to work out well.
And the really great news late last night was that this new functionality has no appreciable cost over 0.2, so FastFormat is still head and shoulders ahead of Boost.Format, Loki.SafeFormat, and the IOStreams in performance (as it also is in robustness, expressiveness and flexibility).
I've released beta 7 of 0.2.1 today, and hope to be releasing an alpha of 0.3 in the next couple of weeks.
- A series of articles to feature in Overload in 09
- Monolith
1. Output a string that uses a single argument twice, with default formatting in each case:
ff::fmtln(std::cout, "x={0}; y={0};", "abc");
This prints x=abc; y=abc;
2. Specify a minimum width for the first parameter:
ff::fmtln(std::cout, "x={0}; y={0,10};", "abc");
This prints x=abc; y= abc;
3. Specify a minimum width and right alignment (which is the default) for the first parameter:
ff::fmtln(std::cout, "x={0}; y={0,10,,>};", "abc");
This prints x=abc; y= abc;
4. Specify a minimum width and left alignment for the first parameter:
ff::fmtln(std::cout, "x={0}; y={0,10,,<};", "abc");
This prints x=abc; y=abc ;
5. Specify a minimum width and centre alignment for the first parameter:
ff::fmtln(std::cout, "x={0}; y={0,10,,^};", "abc");
This prints x=abc; y= abc ;
6. Specify a maximum width for the second parameter:
ff::fmtln(std::cout, "x={0}; y={0,,2};", "abc");
This prints x=abc; y=bc;
7. Specify a maximum width and left alignment for the second parameter:
ff::fmtln(std::cout, "x={0}; y={0,,2,<};", "abc");
This prints x=abc; y=ab;
The precise semantics of the alignment and fill (not shown above) options may still be changed, but the basis of the parameter syntax (four fields: index, min-width, max-width, alignment-and-fill) seem to work out well.
And the really great news late last night was that this new functionality has no appreciable cost over 0.2, so FastFormat is still head and shoulders ahead of Boost.Format, Loki.SafeFormat, and the IOStreams in performance (as it also is in robustness, expressiveness and flexibility).
I've released beta 7 of 0.2.1 today, and hope to be releasing an alpha of 0.3 in the next couple of weeks.
FastFormat now performance tested against Loki
As of 0.2.1 beta 6, FastFormat's performance test programs now also compare against Andrei Alexandrescu's Loki library's SafeFormat component. The results clearly demonstrate FastFormat's performance superiority over this library, as they do over C++'s standard IOStreams and Boost.Format.
Given the fact that FastFormat is more robust and more flexible than these other libraries, and is highly expressive, I think it's fair to now claim that it is the pre-eminent formatting library for C++. All that remains is to provide the planned width+alignment+fill functionality, and it'll be effectively complete.
I'll be looking for input/assistance in the new year for packaging and porting. If anyone wants to volunteer, you'll be most welcome.
Given the fact that FastFormat is more robust and more flexible than these other libraries, and is highly expressive, I think it's fair to now claim that it is the pre-eminent formatting library for C++. All that remains is to provide the planned width+alignment+fill functionality, and it'll be effectively complete.
I'll be looking for input/assistance in the new year for packaging and porting. If anyone wants to volunteer, you'll be most welcome.
Wednesday, December 24, 2008
Merry Christmas ... and a busy New Year
Just wanted to wish everyone a Merry Christmas, and a happy, and busy, New Year.
Definites in the coming year:
- publication of Breaking Up The Monolith; I've been writing like a crazy thing the last week, and now have Part 2 95+% complete and Part 3 ~33% complete
- non-beta release of Pantheios 1.0, followed swiftly by new features
- release of FastFormat 0.3, which will allow width/alignment specification in the format strings
- release of xCover, a code coverage library for C++ (which is already being used in the preparation of STLSoft 1.10)
- release of STLSoft 1.10, which will include new features, and a lot of tidying up and rationalising of the libraries
- a brand new website for Synesis Software, including tutorials and blogs
- new articles and columns from me
See you then.
Matt
Saturday, December 13, 2008
recls 1.9 alpha process begun
I've finally got around to enhancing the old stalwart, recls, which (if you don't know) is a platform-independent file-system recursive-search library.
So, far, I've finished a source-file renaming process that I started over a year ago, and now I'm having a go at the tracing functionality. This was formerly implemented in terms of syslog() on UNIX and OutputDebugString() on Windows. Now, as may be obvious if you follow STLSoft-related library news, I'm thinking it'll be implemented in terms of Pantheios.
The issue here, though, is that there should be no unnecessary coupling between libraries, and I think it would be wrong to couple recls to Pantheios just to provide good logging facilities. So the challenge will be to work out a way to allow the two to work together if required, and to not do so if not. Preprocessor options, function pointers, callbacks, and all manner of other ideas are under consideration.
I'll let you know how it goes. (And you'll be able to see it, as I'm planning to release recls 1.9 alpha 1 as soon as the logging is done.)
So, far, I've finished a source-file renaming process that I started over a year ago, and now I'm having a go at the tracing functionality. This was formerly implemented in terms of syslog() on UNIX and OutputDebugString() on Windows. Now, as may be obvious if you follow STLSoft-related library news, I'm thinking it'll be implemented in terms of Pantheios.
The issue here, though, is that there should be no unnecessary coupling between libraries, and I think it would be wrong to couple recls to Pantheios just to provide good logging facilities. So the challenge will be to work out a way to allow the two to work together if required, and to not do so if not. Preprocessor options, function pointers, callbacks, and all manner of other ideas are under consideration.
I'll let you know how it goes. (And you'll be able to see it, as I'm planning to release recls 1.9 alpha 1 as soon as the logging is done.)
Friday, December 12, 2008
simple_string evolution
I'm finally putting some time into getting stlsoft::(basic_)simple_string finished - it's only been, er, 6 years. :$
Anyhow, here's the thinking:
Anyhow, here's the thinking:
- Round out all the currently-missing std::basic_string equivalency methods, so that it can act as a full functional replacement
- Define its purpose. This is something that has confused STLSoft users over the years.
- Provide compile-time selection of its nature, facilitating user-selected customisations of its implementation
- It has a documented footprint, equivalent to a single char*, which is consistent across platforms/architectures/operating systems, allowing, for example, a user to pass a std::vector<stlsoft::simple_string> to a function taking an array of C-style strings
- It is, in some circumstances, more efficient than std::basic_string. Please note that superior efficiency is not a main design feature of the component, nor is it claimed in any general sense.
Radio silence ...
Having had a protracted break from STLSoft-related blogging, I'm back.
I've been busy with clients, and with various coding and writing projects, of which more in the coming posts.
I'll be attempting to post daily for the next few weeks, as I make my way through the following tasks:
I've been busy with clients, and with various coding and writing projects, of which more in the coming posts.
I'll be attempting to post daily for the next few weeks, as I make my way through the following tasks:
- STLSoft 1.10 alpha releases
- recls 1.9 alpha release
- FastFormat 0.2 beta releases
- Pantheios 1.0 non-beta release
- xCover first release
- ... and other things I've currently forgotten
Friday, November 21, 2008
Using STLSoft 1.10 alpha (delta) releases
Just a quick remark on the strategy for STLSoft 1.10. Essentially, the alpha releases will be forthcoming over the next few months, as components are moved over (and given the treatment) from the 1.9 branch, and new components are added. Each of these alpha releases is in the form of a delta-only distribution, meaning that you will have to first install STLSoft 1.9, and then either:
- Install the STLSoft 1.10 distribution over the top of the 1.9 installation, or
- Install STLSoft 1.10 to a separate area and then use include paths to cause your compiler(s) to see the 1.10 alpha files first
- Defining an STLSOFT environment variable pointing to the 1.9 distro root directory
- Defining an STLSOFT_1_10 environment variable pointing to the 1.10 distro
- Specifying the latter before the former to the compiler, as in "g++ -I$STLSOFT_1_10/include -I$STLSOFT/include my_file.cpp"
STLSoft 1.10 new additions: string_to_integer
Having not posted for over a month, there's a lot to catch up on.
I'm going to start with a series of short missives on the latest additions to STLSoft 1.10. (See this for how to install/use 1.10 alpha (delta) releases.) While I'm catching up with the releases, it's probably not going to go in order, so you'll have to be bear with me.
I'll start today with the new stlsoft::string_to_integer() functions, defined in the new include file stlsoft/conversion/string_to_integer.hpp. These are low-level string->number parsing functions. Their raison d'etre is performance - they'll be used in the forthcoming FastFormat 0.3 version, for high-speed parsing of replacement parameters - and for this they sacrifice some functionality, as discussed below.
They look like the following:
namespace stlsoft
{
int string_to_integer(char const* s, char const** endptr);
int string_to_integer(wchar_t const* s, wchar_t const** endptr);
int string_to_integer(char const* s, size_t len, char const** endptr);
int string_to_integer(wchar_t const* s, size_t len, wchar_t const** endptr);
} // namespace stlsoft
The functions in the first pair take a (non-NULL) pointer to a (nul-terminated) C-style string, along with an optional (may be NULL) pointer to pointer to C-style string, which will receive the pointer to the character terminating the parsing. In this aspect, these functions ape the functionality of the standard strtol() function. However, where they differ is that they do not accept a radix parameter: currently all conversion is decimal.
The second pair do not require nul-terminated strings, and instead use a length parameter. This is useful when parsing numbers out of string types that may not be null-terminated. (Again, this is found in FastFormat, which is the main driver for the release of these functions.)
These functions are very new, and will evolve further before STLSoft 1.10 beta. Three changes that are candidates to be introduced are:
I'll do some specific performance tests on the functions at a later time. They've had some exposure as part of a larger performance test of the forthcoming FastFormat 0.3's new functionality, and have shown to confer a benefit over strtol() and its siblings.
I'm going to start with a series of short missives on the latest additions to STLSoft 1.10. (See this for how to install/use 1.10 alpha (delta) releases.) While I'm catching up with the releases, it's probably not going to go in order, so you'll have to be bear with me.
I'll start today with the new stlsoft::string_to_integer() functions, defined in the new include file stlsoft/conversion/string_to_integer.hpp. These are low-level string->number parsing functions. Their raison d'etre is performance - they'll be used in the forthcoming FastFormat 0.3 version, for high-speed parsing of replacement parameters - and for this they sacrifice some functionality, as discussed below.
They look like the following:
namespace stlsoft
{
int string_to_integer(char const* s, char const** endptr);
int string_to_integer(wchar_t const* s, wchar_t const** endptr);
int string_to_integer(char const* s, size_t len, char const** endptr);
int string_to_integer(wchar_t const* s, size_t len, wchar_t const** endptr);
} // namespace stlsoft
The functions in the first pair take a (non-NULL) pointer to a (nul-terminated) C-style string, along with an optional (may be NULL) pointer to pointer to C-style string, which will receive the pointer to the character terminating the parsing. In this aspect, these functions ape the functionality of the standard strtol() function. However, where they differ is that they do not accept a radix parameter: currently all conversion is decimal.
The second pair do not require nul-terminated strings, and instead use a length parameter. This is useful when parsing numbers out of string types that may not be null-terminated. (Again, this is found in FastFormat, which is the main driver for the release of these functions.)
These functions are very new, and will evolve further before STLSoft 1.10 beta. Three changes that are candidates to be introduced are:
- Supporting different radixes (although for radixes not in [8, 10, 16] it may be that they'd defer to strtol()).
- Support for different integer types. In fact, the four concrete functions are implemented in terms of two function templates whose integral type is a template parameter, so the groundwork is all there
- Support for truncation testing.
I'll do some specific performance tests on the functions at a later time. They've had some exposure as part of a larger performance test of the forthcoming FastFormat 0.3's new functionality, and have shown to confer a benefit over strtol() and its siblings.
Subscribe to:
Posts (Atom)