By using this site, you agree to our updated Privacy Policy and our Terms of Use. Manage your Cookies Settings.
432,498 Members | 1,558 Online
Bytes IT Community
+ Ask a Question
Need help? Post your question and get tips & solutions from a community of 432,498 IT Pros & Developers. It's quick & easy.

Please help with testing & improving a StringValue class

P: n/a
I once suggested in [comp.std.c++] that SomeOne Else(TM) should propose
a string value class that accepted literals and char pointers and so on,
with possible custom deleter, and in case of literal strings just
carrying the original pointer.

In other words, for the simplest usage code:

* no overhead (just carrying a pointer or two), and

* no possibility of exceptions (for that case).

For example, in an exception it's not so good when the string carrier
can throw on construction due to dynamic allocation, and for another
example, passing std::string values around can involve dynamic
allocations and deallocations, and those are very inefficient.

So yesterday evening I fired up an editor and coded a little bit. /Not/
using test-driven development: this code changed significantly as it
clarified! Therefore, there aren't any tests, as yet, and

* it would be nice if some TDD person could device and run proper
tests (it would also be interesting to see them!), and/or

* if other persons could simply try to use this class and report on
bugs, compiler incompatibilities, needed functionality (I don't know
what functionality it should provide in addition to carrying
strings) etc., whatever's relevant.

The code uses boost::intrusive_ptr from the Boost library, which
therefore is required to compile. Strings with embedded zero characters
are not supported in the current code. I don't think the need is great.

Example usage code
StringValue foo()
{
return "No dynamic allocation, no possible exception, fast";
}

StringValue bar()
{
return std::string( "A dynamic" ) + " copy";
}
Example exercising all currently defined constructors, where malloc and
free is used just to demonstrate that also that is possible:

<code>
#include <alfs/StringValueClass.hpp>
#include <iostream>
#include <cstdlib // std::malloc, std::free
#include <cstring // std::strcpy, std::strlen

char const* mallocStr( char const s[] )
{
using namespace std;
return strcpy( static_cast<char*>( malloc( strlen( s ) + 1 ) ), s );
}

void myDeleter( void const* p ) { std::free( const_cast<void*>( p ) ); }

int main()
{
// A StringValue can be freely copied and assigned, but the value
// can not be modified.

using namespace alfs;

char const* const dynValue = "dynamic copy";
char const* const ptrValue = "pointer to persistent buffer";
char const* const customValue = "custom delete";
char const sizedValue[] = { 's', 'i', 'z', 'e', 'd' };

StringValue literal( "literal" ); // No alloc.
StringValue pointer( ptrValue, NoDelete() ); // No alloc.
StringValue custom( mallocStr( customValue ), myDeleter );
StringValue sized( sizedValue, sizeof( sizedValue ) );
StringValue dynamic( dynValue );
StringValue stdval( std::string( "std::string" ) );

std::cout << literal << std::endl;
std::cout << pointer << std::endl;
std::cout << custom << std::endl;
std::cout << sized << std::endl;
std::cout << dynamic << std::endl;
std::cout << stdval << std::endl;
}
</code>

Code currently available (especially if you want to help testing and or
discussing functionality or coding, whatever) at
<url: home.no.net/alfps/cpp/lib/alfs_v00.zip(lawyers, if any: note
that I retain copyright etc., although usage is of course permitted).

Cheers, & hope this can be interesting,

- Alf
Sep 8 '07 #1
Share this Question
Share on Google+
30 Replies


P: n/a
Alf P. Steinbach wrote:
....
StringValue literal( "literal" ); // No alloc.
Is it legal to do:

literal[0] = 'L'; ?
Sep 8 '07 #2

P: n/a
Gianni Mariani wrote:
Alf P. Steinbach wrote:
...
> StringValue literal( "literal" ); // No alloc.

Is it legal to do:

literal[0] = 'L'; ?
int main()
{
// A StringValue can be freely copied and assigned, but the value
// can not be modified.

--
Thanks
Barry
Sep 8 '07 #3

P: n/a
Alf P. Steinbach wrote:
I once suggested in [comp.std.c++] that SomeOne Else(TM) should propose
a string value class that accepted literals and char pointers and so on,
with possible custom deleter, and in case of literal strings just
carrying the original pointer.

In other words, for the simplest usage code:

* no overhead (just carrying a pointer or two), and

* no possibility of exceptions (for that case).

For example, in an exception it's not so good when the string carrier
can throw on construction due to dynamic allocation, and for another
example, passing std::string values around can involve dynamic
allocations and deallocations, and those are very inefficient.

So yesterday evening I fired up an editor and coded a little bit. /Not/
using test-driven development: this code changed significantly as it
clarified! Therefore, there aren't any tests, as yet, and

* it would be nice if some TDD person could device and run proper
tests (it would also be interesting to see them!), and/or

* if other persons could simply try to use this class and report on
bugs, compiler incompatibilities, needed functionality (I don't know
what functionality it should provide in addition to carrying
strings) etc., whatever's relevant.

The code uses boost::intrusive_ptr from the Boost library, which
therefore is required to compile. Strings with embedded zero characters
are not supported in the current code. I don't think the need is great.

Example usage code
StringValue foo()
{
return "No dynamic allocation, no possible exception, fast";
}

StringValue bar()
{
return std::string( "A dynamic" ) + " copy";
}
Example exercising all currently defined constructors, where malloc and
free is used just to demonstrate that also that is possible:

<code>
#include <alfs/StringValueClass.hpp>
#include <iostream>
#include <cstdlib // std::malloc, std::free
#include <cstring // std::strcpy, std::strlen

char const* mallocStr( char const s[] )
{
using namespace std;
return strcpy( static_cast<char*>( malloc( strlen( s ) + 1 ) ), s );
}

void myDeleter( void const* p ) { std::free( const_cast<void*>( p ) ); }

int main()
{
// A StringValue can be freely copied and assigned, but the value
// can not be modified.

using namespace alfs;

char const* const dynValue = "dynamic copy";
char const* const ptrValue = "pointer to persistent buffer";
char const* const customValue = "custom delete";
char const sizedValue[] = { 's', 'i', 'z', 'e', 'd' };

StringValue literal( "literal" ); // No alloc.
StringValue pointer( ptrValue, NoDelete() ); // No alloc.
StringValue custom( mallocStr( customValue ), myDeleter );
StringValue sized( sizedValue, sizeof( sizedValue ) );
StringValue dynamic( dynValue );
StringValue stdval( std::string( "std::string" ) );

std::cout << literal << std::endl;
std::cout << pointer << std::endl;
std::cout << custom << std::endl;
std::cout << sized << std::endl;
std::cout << dynamic << std::endl;
std::cout << stdval << std::endl;
}
</code>

Code currently available (especially if you want to help testing and or
discussing functionality or coding, whatever) at
<url: home.no.net/alfps/cpp/lib/alfs_v00.zip(lawyers, if any: note
that I retain copyright etc., although usage is of course permitted).

Cheers, & hope this can be interesting,
std:: cout << (dynamic == dynValue);

prints 0

as it calls
StringValue::operator char const* ()

so == is to compare pointer other than lexicographical compare

I think it would be a good idea to provide
operator== to make lexicographical compare.

--
Thanks
Barry
Sep 8 '07 #4

P: n/a
* Barry:
Alf P. Steinbach wrote:
>>
char const* const dynValue = "dynamic copy";

std:: cout << (dynamic == dynValue);

prints 0

as it calls
StringValue::operator char const* ()

so == is to compare pointer other than lexicographical compare

I think it would be a good idea to provide
operator== to make lexicographical compare.
Thanks.

This bears thinking about, whether the default semantics should be as
raw pointer (above, plus, may use other pointer operators!) or as
logical string, in which case e.g. concatenation would also be nice.

I'm aware that taking the "as string" semantics to the limit, it becomes
impractical (e.g. locales, infamous Turkish "I"), and that's been used
as an argument for keeping std::string lobotomized. There's a trade-off
here between practicality and surprise surprise for the unwary novice,
and I'm not sure but I think C++ wasn't designed for novices... ;-)
Cheers, & thank you for your constructive feedback,

- Alf
Sep 8 '07 #5

P: n/a
On Sat, 08 Sep 2007 10:21:59 +0200, "Alf P. Steinbach" wrote:
>I once suggested in [comp.std.c++] that SomeOne Else(TM) should propose
a string value class that accepted literals and char pointers and so on,
with possible custom deleter, and in case of literal strings just
carrying the original pointer.

In other words, for the simplest usage code:

* no overhead (just carrying a pointer or two), and

* no possibility of exceptions (for that case).
IOW, you created an assignable but otherwise immutable string class
that provides an optimization for string literals.
>For example, in an exception it's not so good when the string carrier
can throw on construction due to dynamic allocation, and for another
example, passing std::string values around can involve dynamic
allocations and deallocations, and those are very inefficient.

So yesterday evening I fired up an editor and coded a little bit. /Not/
using test-driven development: this code changed significantly as it
clarified! Therefore, there aren't any tests, as yet, and

* it would be nice if some TDD person could device and run proper
tests (it would also be interesting to see them!), and/or
Ahem, well ...
* if other persons could simply try to use this class and report on
bugs, compiler incompatibilities, needed functionality (I don't know
what functionality it should provide in addition to carrying
strings) etc., whatever's relevant.

The code uses boost::intrusive_ptr from the Boost library, which
therefore is required to compile.
If you want your code to be widely used you should get rid of the
Boost dependency (which seems to be no problem in your case).
>Strings with embedded zero characters
are not supported in the current code. I don't think the need is great.

Example usage code
StringValue foo()
{
return "No dynamic allocation, no possible exception, fast";
}

StringValue bar()
{
return std::string( "A dynamic" ) + " copy";
}
In general, the string literal optimization is a good idea. The design
of such a class (template) poses the real challenge. For various
reasons it should hold that sizeof StringValue == sizeof void*. You
need to find a way to distinguish a dynamically allocated array from a
string literal without additional information in the object (not even
an additional flag). One of the reasons for the above is the
requirement of thread safety for string assignment and copying.
Unfortunately there seems to be no way to implement a 'lightweight'
thread-safe assignment operator and/or copy constructor because
incrementing/decrementing the reference-counter and assignment of the
pointer are always two distinct operations. I experimented with my own
string class but could not reach a satisfactory result WRT thread
safety (i.e. when the object is accessed by multiple threads).
>Example exercising all currently defined constructors, where malloc and
free is used just to demonstrate that also that is possible:

<code>
#include <alfs/StringValueClass.hpp>
#include <iostream>
#include <cstdlib // std::malloc, std::free
#include <cstring // std::strcpy, std::strlen

char const* mallocStr( char const s[] )
{
using namespace std;
return strcpy( static_cast<char*>( malloc( strlen( s ) + 1 ) ), s );
}

void myDeleter( void const* p ) { std::free( const_cast<void*>( p ) ); }

int main()
{
// A StringValue can be freely copied and assigned, but the value
// can not be modified.

using namespace alfs;

char const* const dynValue = "dynamic copy";
char const* const ptrValue = "pointer to persistent buffer";
char const* const customValue = "custom delete";
char const sizedValue[] = { 's', 'i', 'z', 'e', 'd' };

StringValue literal( "literal" ); // No alloc.
StringValue pointer( ptrValue, NoDelete() ); // No alloc.
StringValue custom( mallocStr( customValue ), myDeleter );
StringValue sized( sizedValue, sizeof( sizedValue ) );
StringValue dynamic( dynValue );
StringValue stdval( std::string( "std::string" ) );

std::cout << literal << std::endl;
std::cout << pointer << std::endl;
std::cout << custom << std::endl;
std::cout << sized << std::endl;
std::cout << dynamic << std::endl;
std::cout << stdval << std::endl;
}
</code>

Code currently available (especially if you want to help testing and or
discussing functionality or coding, whatever) at
<url: home.no.net/alfps/cpp/lib/alfs_v00.zip(lawyers, if any: note
that I retain copyright etc., although usage is of course permitted).
A lot of established Open Source licenses like MIT, new BSD
(http://www.opensource.org/licenses/alphabetical) or ISC
(http://en.wikipedia.org/wiki/ISC_license) are available.
--
Roland Pibinger
"The best software is simple, elegant, and full of drama" - Grady Booch
Sep 8 '07 #6

P: n/a
Alf P. Steinbach wrote:
I once suggested in [comp.std.c++] that SomeOne Else(TM) should propose
a string value class that accepted literals and char pointers and so on,
with possible custom deleter, and in case of literal strings just
carrying the original pointer.

In other words, for the simplest usage code:

* no overhead (just carrying a pointer or two), and

* no possibility of exceptions (for that case).

For example, in an exception it's not so good when the string carrier
can throw on construction due to dynamic allocation, and for another
example, passing std::string values around can involve dynamic
allocations and deallocations, and those are very inefficient.

So yesterday evening I fired up an editor and coded a little bit. /Not/
using test-driven development: this code changed significantly as it
clarified! Therefore, there aren't any tests, as yet, and

* it would be nice if some TDD person could device and run proper
tests (it would also be interesting to see them!), and/or

* if other persons could simply try to use this class and report on
bugs, compiler incompatibilities, needed functionality (I don't know
what functionality it should provide in addition to carrying
strings) etc., whatever's relevant.

The code uses boost::intrusive_ptr from the Boost library, which
therefore is required to compile. Strings with embedded zero characters
are not supported in the current code. I don't think the need is great.

Example usage code
StringValue foo()
{
return "No dynamic allocation, no possible exception, fast";
}

StringValue bar()
{
return std::string( "A dynamic" ) + " copy";
}
Example exercising all currently defined constructors, where malloc and
free is used just to demonstrate that also that is possible:

<code>
#include <alfs/StringValueClass.hpp>
#include <iostream>
#include <cstdlib // std::malloc, std::free
#include <cstring // std::strcpy, std::strlen

char const* mallocStr( char const s[] )
{
using namespace std;
return strcpy( static_cast<char*>( malloc( strlen( s ) + 1 ) ), s );
}

void myDeleter( void const* p ) { std::free( const_cast<void*>( p ) ); }

int main()
{
// A StringValue can be freely copied and assigned, but the value
// can not be modified.

using namespace alfs;

char const* const dynValue = "dynamic copy";
char const* const ptrValue = "pointer to persistent buffer";
char const* const customValue = "custom delete";
char const sizedValue[] = { 's', 'i', 'z', 'e', 'd' };

StringValue literal( "literal" ); // No alloc.
StringValue pointer( ptrValue, NoDelete() ); // No alloc.
StringValue custom( mallocStr( customValue ), myDeleter );
StringValue sized( sizedValue, sizeof( sizedValue ) );
StringValue dynamic( dynValue );
StringValue stdval( std::string( "std::string" ) );

std::cout << literal << std::endl;
std::cout << pointer << std::endl;
std::cout << custom << std::endl;
std::cout << sized << std::endl;
std::cout << dynamic << std::endl;
std::cout << stdval << std::endl;
}
</code>

Code currently available (especially if you want to help testing and or
discussing functionality or coding, whatever) at
<url: home.no.net/alfps/cpp/lib/alfs_v00.zip(lawyers, if any: note
that I retain copyright etc., although usage is of course permitted).
I tried a look at SharedArray,

template <typename T>
void Deleter(T const* p)
{
delete[] p;
}

SharedArray<intintArr(new int[10], Deleter<int>);

and have a watch on the data:

- intArr {myRcArray={...} myArray=0x00382b58 } alfs::SharedArray<int>
- myRcArray {p_=0x00382848 }

....

myRefCount 1 unsigned long
- myPointer 0x00382b58 int *
-842150451 int
- myArray 0x00382b58 int *
-842150451 int
it seems that myArray is always equal to myPointer
So is it redundant?

--
Thanks
Barry
Sep 8 '07 #7

P: n/a
* Roland Pibinger:
On Sat, 08 Sep 2007 10:21:59 +0200, "Alf P. Steinbach" wrote:
>I once suggested in [comp.std.c++] that SomeOne Else(TM) should propose
a string value class that accepted literals and char pointers and so on,
with possible custom deleter, and in case of literal strings just
carrying the original pointer.

In other words, for the simplest usage code:

* no overhead (just carrying a pointer or two), and

* no possibility of exceptions (for that case).

IOW, you created an assignable but otherwise immutable string class
that provides an optimization for string literals.
And also for the case of passing around a string with a custom delete
operation, e.g. as provided by many API functions such as Windows'
command line parsing.

When using std::string or std::wstring for this, the API function's
string must first be copied, where dynamic allocation is used, and then
freed (using its own delete operation). This is costly. Then, when
that string should be passed to an API function again, the std::string
must sometimes be copied to dynamically allocated memory using the API's
allocator. Which might happen many times for the same string. This is
costly. A string value class with custom deleter, such as StringValue,
solves that problem. No costly dynamic allocations, and no O(n) copy
operations, for the cases where such operations can be dispensed with by
keeping a delete function along with the string value.

Of course the last can also be accomplished using e.g.
boost::shared_ptr. But then different kinds of strings have to be
treated differently, with conversion among them. And it's awkward
anyway, so awkward that I don't think anybody's done exactly that.
[snip]
>>
The code uses boost::intrusive_ptr from the Boost library, which
therefore is required to compile.

If you want your code to be widely used you should get rid of the
Boost dependency (which seems to be no problem in your case).
It think most C++ programmers have the Boost library installed.

But since that's a huge library, it would perhaps be an idea to bundle
the one or few Boost files that's actually used?

intrusive_ptr is just header file code, not separate compilation.

>Strings with embedded zero characters
are not supported in the current code. I don't think the need is great.

Example usage code
StringValue foo()
{
return "No dynamic allocation, no possible exception, fast";
}

StringValue bar()
{
return std::string( "A dynamic" ) + " copy";
}

In general, the string literal optimization is a good idea. The design
of such a class (template) poses the real challenge. For various
reasons it should hold that sizeof StringValue == sizeof void*. You
need to find a way to distinguish a dynamically allocated array from a
string literal without additional information in the object (not even
an additional flag). One of the reasons for the above is the
requirement of thread safety for string assignment and copying.
Unfortunately there seems to be no way to implement a 'lightweight'
thread-safe assignment operator and/or copy constructor because
incrementing/decrementing the reference-counter and assignment of the
pointer are always two distinct operations. I experimented with my own
string class but could not reach a satisfactory result WRT thread
safety (i.e. when the object is accessed by multiple threads).
Uhm, that's a different problem. Essentially, if I understand you
correctly, the problem is what trade-off can you do so that in the case
of multi-threaded access to the same string, the total cost of safe
copying is less than with a mutex or whatever? And I think the best
answer is to /not/ accept the premise that multi-threaded access to the
same string without some external thread synchronization such as a
mutex, is something one should support: instead, avoid it!

I think it's in the same league as designing a language to support
arbitrary gotos. That would restrict the language severely (e.g., gotos
past object construction renders all construction guarantees void, so to
support arbitrary gotos, no object construction guarantees). And
instead of designing the language with the goal of supporting
unrestricted gotos, the sensible course is IMHO to restrict gotos.

>Example exercising all currently defined constructors, where malloc and
free is used just to demonstrate that also that is possible:

<code>
#include <alfs/StringValueClass.hpp>
#include <iostream>
#include <cstdlib // std::malloc, std::free
#include <cstring // std::strcpy, std::strlen

char const* mallocStr( char const s[] )
{
using namespace std;
return strcpy( static_cast<char*>( malloc( strlen( s ) + 1 ) ), s );
}

void myDeleter( void const* p ) { std::free( const_cast<void*>( p ) ); }

int main()
{
// A StringValue can be freely copied and assigned, but the value
// can not be modified.

using namespace alfs;

char const* const dynValue = "dynamic copy";
char const* const ptrValue = "pointer to persistent buffer";
char const* const customValue = "custom delete";
char const sizedValue[] = { 's', 'i', 'z', 'e', 'd' };

StringValue literal( "literal" ); // No alloc.
StringValue pointer( ptrValue, NoDelete() ); // No alloc.
StringValue custom( mallocStr( customValue ), myDeleter );
StringValue sized( sizedValue, sizeof( sizedValue ) );
StringValue dynamic( dynValue );
StringValue stdval( std::string( "std::string" ) );

std::cout << literal << std::endl;
std::cout << pointer << std::endl;
std::cout << custom << std::endl;
std::cout << sized << std::endl;
std::cout << dynamic << std::endl;
std::cout << stdval << std::endl;
}
</code>

Code currently available (especially if you want to help testing and or
discussing functionality or coding, whatever) at
<url: home.no.net/alfps/cpp/lib/alfs_v00.zip(lawyers, if any: note
that I retain copyright etc., although usage is of course permitted).

A lot of established Open Source licenses like MIT, new BSD
(http://www.opensource.org/licenses/alphabetical) or ISC
(http://en.wikipedia.org/wiki/ISC_license) are available.
Thank you.

I think I heard something about the Apache license, too.

Cheers, & thanks for your constructive feedback (much I hadn't thought
about!),

- ALf
Sep 8 '07 #8

P: n/a
* Barry:
>
I tried a look at SharedArray,

template <typename T>
void Deleter(T const* p)
{
delete[] p;
}

SharedArray<intintArr(new int[10], Deleter<int>);

and have a watch on the data:

- intArr {myRcArray={...} myArray=0x00382b58 }
alfs::SharedArray<int>
- myRcArray {p_=0x00382848 }

...

myRefCount 1 unsigned long
- myPointer 0x00382b58 int *
-842150451 int
- myArray 0x00382b58 int *
-842150451 int
it seems that myArray is always equal to myPointer
So is it redundant?
'myArray' is logically redundant when there is reference counting
(dynamic allocation), but in that case provides a few nano-seconds
faster access...

'myArray' is needed for the case where there is no reference counting,
i.e. for the case of no dynamic allocation -- and that's also the
reason why boost::intrusive_ptr is used instead of boost::shared_array
(which always uses or may use dynamic allocation).

And that is half of what this class is about: avoiding those pesky
dynamic allocations and O(n) copy operations where they're not needed.
Copy construction and assignment is always constant time with no
inefficient dynamic allocation. I think that's a nice feature. :-)

Cheers,

- Alf
Sep 8 '07 #9

P: n/a
Alf P. Steinbach wrote:
....
Comments, ideas, criticism etc. welcome!
Is it thread safe?
Sep 9 '07 #10

P: n/a
* Gianni Mariani:
Alf P. Steinbach wrote:
...
>Comments, ideas, criticism etc. welcome!

Is it thread safe?
As much as any general class. ;-)

I don't believe in adding mutual exclusion to basic operations for a
general purpose class, but I understand the question: with non-mutable
values, it's natural to wonder whether that's in support of threading.

As I see it, adding mutual exclusion everywhere is inefficient, drags in
a lot of threading support code not otherwise needed, and leads to
spaghetti design when people feel "safe" doing anything whatsoever in
threads: IMHO threading code needs to be designed with care, not
accessing variables in other threads willy-nilly.

Cheers,

- Alf
PS: A more serious question: is it slicing safe? Version 01 is not, in
the sense that it's possible to use references or pointers so that one
ends up with a StringValue that's null (with value copied from a
StringValueOrNull). But I'm working on that, namely having
StringValueOrNull and StringValue derive from a common abstract base,
instead of having StringValue derive from StringValueOrNull. Now if
only I could make SFINAE & boost::disable_if work properly! :-)
Sep 9 '07 #11

P: n/a
Alf P. Steinbach wrote:
I once suggested in [comp.std.c++] that SomeOne Else(TM) should propose
Alf,
I made some modification to v00

Aim:
Use policy for `DeleteFunc' template parameter for `SharedArray'
the DeleteFunc policy class should contain
static void Detete(T* p);

then `SharedArray' has no need to store the free function as member

I'll mail you the code that I modify, since the modification is sparse.


--
Thanks
Barry
Sep 9 '07 #12

P: n/a
* Barry:
Alf P. Steinbach wrote:
>I once suggested in [comp.std.c++] that SomeOne Else(TM) should propose

Alf,
I made some modification to v00

Aim:
Use policy for `DeleteFunc' template parameter for `SharedArray'
the DeleteFunc policy class should contain
static void Detete(T* p);

then `SharedArray' has no need to store the free function as member

I'll mail you the code that I modify, since the modification is sparse.
I got your code before I read the above explanation. I struggled a bit
to make it report memory leaks (why the &%/(()! doesn't that code
report leaks?), since I didn't notice you'd added a policy. But did
notice the main program changes, which "should" have caused some leaks...

It saves four or eight bytes (32-bit/64-bit machine) per ref-counted
instance, at the (marginal) cost of requiring a free deleter function to
have extern linkage or be wrapped in a class.

Extern linkage: I'm thinking any free extern linkage function can be
automatically mapped to a suitable functor class; no need for client
code to define them, at least not if I'm not muddled in my thinking.
Cheers, & thanks,

- Alf
Sep 9 '07 #13

P: n/a
Alf P. Steinbach wrote:
* Barry:
>Alf P. Steinbach wrote:
>>I once suggested in [comp.std.c++] that SomeOne Else(TM) should propose

Alf,
I made some modification to v00

Aim:
Use policy for `DeleteFunc' template parameter for `SharedArray'
the DeleteFunc policy class should contain
static void Detete(T* p);

then `SharedArray' has no need to store the free function as member

I'll mail you the code that I modify, since the modification is sparse.

I got your code before I read the above explanation. I struggled a bit
to make it report memory leaks (why the &%/(()! doesn't that code
report leaks?), since I didn't notice you'd added a policy. But did
notice the main program changes, which "should" have caused some leaks...

It saves four or eight bytes (32-bit/64-bit machine) per ref-counted
instance, at the (marginal) cost of requiring a free deleter function to
have extern linkage or be wrapped in a class.

Extern linkage: I'm thinking any free extern linkage function can be
automatically mapped to a suitable functor class; no need for client
code to define them, at least not if I'm not muddled in my thinking.

Well, when I was doing that policy design
I came to a question
* Use static function or operator()

choose the latter one, then I have to make an instance of the functor
before calling, just like the tr1::hash. It looks awful, but it has one
advantage, it's stateful, even more, we can then use tr1::bind to form
any functor.

But in this case, no storing the functor makes the statefulness
impossible, I think a static function is enough, so I made this choice.

--
Thanks
Barry
Sep 9 '07 #14

P: n/a

On 9/9/07 12:59 AM, in article 13*************@corp.supernews.com, "Alf P.
Steinbach" <al***@start.nowrote:

An 01 version of StringValue is now available at
<url: http://home.no.net/alfps/cpp/lib/alfs_v01.zip>.
New features:

* A license reference (Boost license) is included in every file,
resulting from comments by Roland Pibinger (thanks).

* operator==, operator< added,
resulting from comments by Barry <dh***@2000.com(thanks).

* Implicit conversion to 'char const*' /removed/, because

* StringValue is now a subclass of a new class StringValueOrNull,
which supports passing null-values around. A StringValue is
therefore implicitly convertible to StringValueOrNull. A
StringValueOrNull value can only be explicitly converted to pure
StringValue, then with checking of nullvalue & possible exception.

* Support for << and >stream i/o added (because of removal of
implicit conversion to 'char const*').

Comments, ideas, criticism etc. welcome!
Overall, the StringValue class looks very well-designed and implemented.

I do have two small criticisms. First, the StringValue and SharedArray
swap() routines should not be declared in the std:: namespace. As the
comment points out, declaring these swap() routines in the std namespace is
a no-no. Besides, the std namespace is not the optimal place to declare a
swap() routine for a user-defined type - because only those routines in the
std namespace are likely to find it.

A better place to declare a user-defined swap() routine (and where the
Standard actually expects to find it) would be in the same namespace as the
type being swapped (in this case, the "alfs" namespace). Declaring the
swap() routine in the alfs namespace would mean that all routines - not just
those in the std namespace - should be able find it (courtesy of
argument-dependent lookup (ADL)).

Second, I don't see how the concept of "null" is compatible with a
StringValue class (with the emphasis on "value"). In other words, a
StringValue object should have a value (even if it's just an empty string).
The last thing that most C++ programmers want from a utility class is a
"special" valued object of that class that has to be treated differently
than all of the others. And if the program needs to distinguish a null
pointer from an empty string, then it should do so before instantiating a
StringValue object.

So my suggestion is that a StringValue object initialized with a null
pointer constant should either throw an exception (probably not warranted)
or treat the null pointer as an empty string (probably the safer, more
reasonable behavior). After all, the Standard Library's own string class,
std::string, has no concept of a "null" string - and I have never known
anyone to complain about its absence.

Greg
Sep 9 '07 #15

P: n/a
* Greg Herlihy:
On 9/9/07 12:59 AM, in article 13*************@corp.supernews.com, "Alf P.
Steinbach" <al***@start.nowrote:

>An 01 version of StringValue is now available at
<url: http://home.no.net/alfps/cpp/lib/alfs_v01.zip>.
New features:

* A license reference (Boost license) is included in every file,
resulting from comments by Roland Pibinger (thanks).

* operator==, operator< added,
resulting from comments by Barry <dh***@2000.com(thanks).

* Implicit conversion to 'char const*' /removed/, because

* StringValue is now a subclass of a new class StringValueOrNull,
which supports passing null-values around. A StringValue is
therefore implicitly convertible to StringValueOrNull. A
StringValueOrNull value can only be explicitly converted to pure
StringValue, then with checking of nullvalue & possible exception.

* Support for << and >stream i/o added (because of removal of
implicit conversion to 'char const*').

Comments, ideas, criticism etc. welcome!

Overall, the StringValue class looks very well-designed and implemented.

I do have two small criticisms. First, the StringValue and SharedArray
swap() routines should not be declared in the std:: namespace. As the
comment points out, declaring these swap() routines in the std namespace is
a no-no. Besides, the std namespace is not the optimal place to declare a
swap() routine for a user-defined type - because only those routines in the
std namespace are likely to find it.

A better place to declare a user-defined swap() routine (and where the
Standard actually expects to find it) would be in the same namespace as the
type being swapped (in this case, the "alfs" namespace). Declaring the
swap() routine in the alfs namespace would mean that all routines - not just
those in the std namespace - should be able find it (courtesy of
argument-dependent lookup (ADL)).
Thanks, I'll try that!

Or at least, think about it.

My original thinking was that client code that does

std::swap( a, b );

should just work -- efficiently. But that thinking was not deep
enough. Because with StringValue, assignment, which is probably what
that will result in without overloading that function, is constant time
and with no dynamic allocation, i.e. very efficient anyway.

But it's a shame that std::swap doesn't automatically adjust to classes
with swap member function defined (would an implementation that did
that, be conforming?).

And ditto for std::sort.

Second, I don't see how the concept of "null" is compatible with a
StringValue class (with the emphasis on "value"). In other words, a
StringValue object should have a value (even if it's just an empty string).
The last thing that most C++ programmers want from a utility class is a
"special" valued object of that class that has to be treated differently
than all of the others. And if the program needs to distinguish a null
pointer from an empty string, then it should do so before instantiating a
StringValue object.

So my suggestion is that a StringValue object initialized with a null
pointer constant should either throw an exception (probably not warranted)
It does (and did).

or treat the null pointer as an empty string (probably the safer, more
reasonable behavior).
No, that's what the StringValueOrNull class is for: with pure
StringValue I think it's better to catch that as an error or failure (it
also asserts). StringValueOrNull is not meant as a full-featured class.
It's just available as a "compatible" carrier class for the cases
where you need to distinguish null-values, such as from a database lookup.

Or perhaps to cater to the requirements of old C code.

And I finally got the SFINAE / boost::disable_if stuff to work, so now
these two classes, StringValueOrNull and StringValue, are siblings, with
implicit conversion to StringValueOrNull, and only explicit conversion
the other way, then with checking of nullvalue and possible exception
(both conversions constant time no dynamic allocation).

After all, the Standard Library's own string class,
std::string, has no concept of a "null" string - and I have never known
anyone to complain about its absence.
Oh, they do... :-)

Except the "solution" is commonly to use C strings and pointers (ugh).

And one funny solution in one project, in Java, encoding the null value
as a special UUID string value (no fuss with transferring over Corba).
Cheers, and thanks,

- Alf
Sep 9 '07 #16

P: n/a
* Barry:
>>
Well, when I was doing that policy design
I came to a question
* Use static function or operator()

choose the latter one, then I have to make an instance of the functor
before calling, just like the tr1::hash. It looks awful, but it has one
advantage, it's stateful, even more, we can then use tr1::bind to form
any functor.

But in this case, no storing the functor makes the statefulness
impossible, I think a static function is enough, so I made this choice.
On reflection, I think what you mention here about statefulness is
important. Using the policy based approach could be a premature
optimization. After all, there probably will not be that many instances
around at a time... :-)

Btw., I've posted the latest, which now has wide string support (see
else-thread).

Cheers, & thanks,

- Alf
Sep 9 '07 #17

P: n/a
On Sep 9, 12:56 pm, "Alf P. Steinbach" <al...@start.nowrote:
(I'm wondering whether SharedArray should provide indexing, and/or
perhaps keep track of the length of the array: perplexingly and almost
paradoxically, it hasn't been needed. I'm also wondering whether there
is some better way to steer constructor selection (in StringValue and
StringValueOrNull) the Right Way, currently using boost::disable_if?)
One idea to help prevent StringValue's constructor from being passed a
const char array when a string literal is expected, would be to offer
a "StringLiteral" (or STRING_LITERAL) macro that clients could use to
designate the string literal initializer explicitly. (This suggestion
is based on a similar macro in Apple's CFString class.)

#define StringLiteral(a) StringValue(""a)

The double-quotes will cause a compile-time error - unless the
initializer "a" is a string literal (that is, it has double-quotes
surrounding it):

StringValue f()
{
const char s[] = "some text";

return StringLiteral(s); // Error: expected primary-expression
before '('

return StringLiteral("some text"); // OK
}

and alternately:

StringValue sv( StringLiteral("a string literal"));

Although I am not a big fan of macros, I will admit that they
occassionally have their uses.

Greg
Sep 10 '07 #18

P: n/a
"Roland Pibinger" <rp*****@yahoo.comwrote in message
news:46**************@news.utanet.at...
[...]
Unfortunately there seems to be no way to implement a 'lightweight'
thread-safe assignment operator and/or copy constructor because
incrementing/decrementing the reference-counter and assignment of the
pointer are always two distinct operations.
[...]

You could use DWCAS.

Sep 10 '07 #19

P: n/a
* Greg Herlihy:
On Sep 9, 12:56 pm, "Alf P. Steinbach" <al...@start.nowrote:
>(I'm wondering whether SharedArray should provide indexing, and/or
perhaps keep track of the length of the array: perplexingly and almost
paradoxically, it hasn't been needed. I'm also wondering whether there
is some better way to steer constructor selection (in StringValue and
StringValueOrNull) the Right Way, currently using boost::disable_if?)

One idea to help prevent StringValue's constructor from being passed a
const char array when a string literal is expected, would be to offer
a "StringLiteral" (or STRING_LITERAL) macro that clients could use to
designate the string literal initializer explicitly. (This suggestion
is based on a similar macro in Apple's CFString class.)

#define StringLiteral(a) StringValue(""a)

The double-quotes will cause a compile-time error - unless the
initializer "a" is a string literal (that is, it has double-quotes
surrounding it):

StringValue f()
{
const char s[] = "some text";

return StringLiteral(s); // Error: expected primary-expression
before '('

return StringLiteral("some text"); // OK
}

and alternately:

StringValue sv( StringLiteral("a string literal"));

Although I am not a big fan of macros, I will admit that they
occassionally have their uses.
Thanks, it's an ingenious idea.

But I don't think it helps with respect to technical detection of
non-literals, because if/when the macro is used one already knows that
the argument is a literal.

I mean, what's the chance of anyone writing STRING_LITERAL( b ) where b
is a local array, and the macro helping to catch this mistake?
Cheers, & thanks anyway (even though I'm disagreeing! ;-)),

- Alf
Sep 10 '07 #20

P: n/a
* Alf P. Steinbach:
>
An 02 version of StringValue is now available at
<url: http://home.no.net/alfps/cpp/lib/alfs_v02.zip>.
An 03 version of StringValue is now available at
<url: http://home.no.net/alfps/cpp/lib/alfs_v03.zip>.

This version is mainly just a name change, reflecting that wchar_t is
now the /natural/ character code type:

Old name: New name:

WStringValueOrNull StringValueOrNull
WStringValue StringValue

StringValueOrNull BStringValueOrNull
StringValue BStringValue

E.g., in modern Windows programming wchar_t is the default choice (at
least for me), and then it's just a hassle, and makes for unreadable
code, to have "W" and "w" prefixes all over the place, on every name.

I also added a file [acknowledgments.txt].

TESTING:
Could readers please try to sort a large vector and large list of e.g.
BStringValue versus std::string, and report the timings? I'm suspecting
that at least for vector of strings, unoptimized BStringValue will be
significantly faster than a typical heavily optimized by best experts
around std::string. But this is just a hunch... ;-)

Cheers, & hope this can be interesting[1],

- Alf
Notes:
[1] I'm considering adding support for "tied" string values (e.g. a
string value that is just a pointer from Windows' argument list, but
keeping a reference count updated for that list), and += concatenation,
the latter because there's so much O(n^2) string concatenation code
around, and although values are immutable, += can do the equivalent of s
= s + t, only much more efficient with O(t) amortized copying time. I'm
not sure yet whether these two features are mutually incompatible!
Sep 10 '07 #21

P: n/a
Alf P. Steinbach wrote:
* Alf P. Steinbach:
>>
TESTING:
Could readers please try to sort a large vector and large list of e.g.
BStringValue versus std::string, and report the timings? I'm suspecting
that at least for vector of strings, unoptimized BStringValue will be
significantly faster than a typical heavily optimized by best experts
around std::string. But this is just a hunch... ;-)
You must forget to test the 'W' streaming of StringValue
change them into basic_ostream.
--
Thanks
Barry
Sep 10 '07 #22

P: n/a
* Barry:
Alf P. Steinbach wrote:
>* Alf P. Steinbach:
>>>
>TESTING:
Could readers please try to sort a large vector and large list of e.g.
BStringValue versus std::string, and report the timings? I'm
suspecting that at least for vector of strings, unoptimized
BStringValue will be significantly faster than a typical heavily
optimized by best experts around std::string. But this is just a
hunch... ;-)

You must forget to test the 'W' streaming of StringValue
change them into basic_ostream.
Not sure what you mean. Wide string streaming operators << and >are
not provided because MingW g++ 3.4.4 doesn't support wide streams. That
is, MingW g++ 3.4.4 simply does not implement wide streams, e.g. there's
no wcout or wcin, although std::wstring is implemented.

However, narrow string streaming operators (for BStringValue) are
provided, and if the compiler supports wide streams, wide strings
(StringValue) can be streamed out simply by applying the pointer()
member function -- and streaming operators implemented simply by
copying and changing the types for the existing narrow operators.

More generally: when streaming wide character strings using the standard
library streams (no matter whether to/from std::wstring or what), one
should be aware that mostly the result is a not-very-well-defined
conversion to narrow characters. I.e. "wcout << L"hello" << endl" does
not by default result in UTF-16 or some other encoding at the output
end. And the standard library does not offer such encodings.

As far as I know, only the Dinkumware library has reasonable support for
wide character streaming, in the sense of offering the usual Unicode
encoding formats.

It would be nice if e.g. Boost provided this...
Cheers,

- Alf (hoping SomeOne(TM) will do the honors of measuring performance!)
Sep 10 '07 #23

P: n/a
On Mon, 10 Sep 2007 20:17:14 +0200, "Alf P. Steinbach" wrote:
>Wide string streaming operators << and >are
not provided because MingW g++ 3.4.4 doesn't support wide streams. That
is, MingW g++ 3.4.4 simply does not implement wide streams, e.g. there's
no wcout or wcin, although std::wstring is implemented.
Try to define _GLIBCXX_USE_WCHAR_T

--
Roland Pibinger
"The best software is simple, elegant, and full of drama" - Grady Booch
Sep 10 '07 #24

P: n/a
* Roland Pibinger:
On Mon, 10 Sep 2007 20:17:14 +0200, "Alf P. Steinbach" wrote:
>Wide string streaming operators << and >are
not provided because MingW g++ 3.4.4 doesn't support wide streams. That
is, MingW g++ 3.4.4 simply does not implement wide streams, e.g. there's
no wcout or wcin, although std::wstring is implemented.

Try to define _GLIBCXX_USE_WCHAR_T
I think I tried that once.

Anyway, if it worked (with no harmful side-effects), then wide streams
would be supported by default. One doesn't remove a large portion of
the standard library for no reason.

So I doubt it works, at least not without harmful side-effects.

Cheers,

- Alf
Sep 10 '07 #25

P: n/a
Alf P. Steinbach wrote:
* Barry:
>Alf P. Steinbach wrote:
>>* Alf P. Steinbach:
>>TESTING:
Could readers please try to sort a large vector and large list of
e.g. BStringValue versus std::string, and report the timings? I'm
suspecting that at least for vector of strings, unoptimized
BStringValue will be significantly faster than a typical heavily
optimized by best experts around std::string. But this is just a
hunch... ;-)

You must forget to test the 'W' streaming of StringValue
change them into basic_ostream.

Not sure what you mean. Wide string streaming operators << and >are
not provided because MingW g++ 3.4.4 doesn't support wide streams. That
is, MingW g++ 3.4.4 simply does not implement wide streams, e.g. there's
no wcout or wcin, although std::wstring is implemented.
I didn't know this, I'm quite dummy on iostream.
So "have templated iostream or not" is equivalent to "wide stream support"?

--
Thanks
Barry
Sep 11 '07 #26

P: n/a
* Barry:
Alf P. Steinbach wrote:
>* Barry:
>>Alf P. Steinbach wrote:
* Alf P. Steinbach:
>

TESTING:
Could readers please try to sort a large vector and large list of
e.g. BStringValue versus std::string, and report the timings? I'm
suspecting that at least for vector of strings, unoptimized
BStringValue will be significantly faster than a typical heavily
optimized by best experts around std::string. But this is just a
hunch... ;-)
You must forget to test the 'W' streaming of StringValue
change them into basic_ostream.

Not sure what you mean. Wide string streaming operators << and >are
not provided because MingW g++ 3.4.4 doesn't support wide streams.
That is, MingW g++ 3.4.4 simply does not implement wide streams, e.g.
there's no wcout or wcin, although std::wstring is implemented.

I didn't know this, I'm quite dummy on iostream.
Well, wide character streaming requires conversion to/from some
encoding, and presumably that's why it's missing in g++ 3.4.4.

So "have templated iostream or not" is equivalent to "wide stream support"?
I assume you mean, why didn't I templatize the operators?

Work... ;-)

But OK OK OK, did that, since if you react others are also likely to
react, but I think I'll wait posting a new version until I've
implemented some more complete functionality (like tying, great for
constant time substrings, and perhaps also efficient concatenation).
Cheers, & thanks,

- Alf (still hoping SomeOne(TM) can do the honors of testing speed!)
Sep 11 '07 #27

P: n/a
"Alf P. Steinbach" <al***@start.nowrote in message
news:13*************@corp.supernews.com...
>I once suggested in [comp.std.c++] that SomeOne Else(TM) should propose a
string value class that accepted literals and char pointers and so on, with
possible custom deleter, and in case of literal strings just carrying the
original pointer.
[...]

You could use a lock-based and/or lock-free reader-writer pattern with your
code. For instance, here is a proxy garbage collector that can work well
within the concept: any object construction results in a subsequent
destruction. The proxy collector manages the lifetime that represents the
period in time between the ctor and dtor:

http://home.comcast.net/~vzoom/demos/pc_sample.c
(vc-6.0 code for an x86)

Look here for some further info:

http://groups.google.com/group/comp....&q=pc_sample.c
Sep 11 '07 #28

P: n/a
"Chris Thomasson" <cr*****@comcast.netwrote in message
news:r-******************************@comcast.com...
"Alf P. Steinbach" <al***@start.nowrote in message
news:13*************@corp.supernews.com...
>>I once suggested in [comp.std.c++] that SomeOne Else(TM) should propose a
string value class that accepted literals and char pointers and so on,
with possible custom deleter, and in case of literal strings just carrying
the original pointer.
[...]

You could use a lock-based and/or lock-free reader-writer pattern with
your code. For instance, here is a proxy garbage collector that can work
well within the concept:
[...]

One other thing, the pattern that results from using a read-write lock is
compatible with the overall reader-writer pattern. One pattern can cover
lock-based algorithms and lock-free algorithms; very good. You can maintain
"portability" by using POSIX to create a failsafe implementation of a
"standard in-house" interface.

Sep 11 '07 #29

P: n/a
Chris Thomasson" <cr*****@comcast.net>
[...]
One other thing, the pattern that results from using a read-write lock is
compatible with the overall reader-writer pattern. One pattern can cover
lock-based algorithms and lock-free algorithms; very good. You can
maintain "portability" by using POSIX to create a failsafe implementation
of a "standard in-house" interface.
[...]

Read this for simple example of a fairly efficient marriage between
lock-free and lock-based programming:

https://coolthreads.dev.java.net/ser...essageID=11068

Notice the pattern with the memory barriers. The producer of a string most
likely will need to execute at least a #StoreStore memory barrier
instruction before it produces data into a shared location. The blending of
lock-free and lock-based occurs within the critical-section of the lock. The
code is effectively publishing data into a public place that has visitors
which choose to access the data without the help of the lock. The readers
are just loading pointers, executing an implied and/or explicit "dependant"
#LoadLoad style memory-barrier, dereference the pointers they previously
loaded and issuing "subsequent logic" on the resulting data.

Sep 11 '07 #30

P: n/a
On Sun, 9 Sep 2007 21:14:10 -0700, "Chris Thomasson" wrote:
>"Roland Pibinger" <rp*****@yahoo.comwrote in message
>Unfortunately there seems to be no way to implement a 'lightweight'
thread-safe assignment operator and/or copy constructor because
incrementing/decrementing the reference-counter and assignment of the
pointer are always two distinct operations.

You could use DWCAS.
I could if I were an expert in DCAS like you!
--
Roland Pibinger
"The best software is simple, elegant, and full of drama" - Grady Booch
Sep 11 '07 #31

This discussion thread is closed

Replies have been disabled for this discussion.