For Programmers: Free Programming Magazines  


Home > Archive > VC STL > February 2006 > Problems with ctype customization









You are viewing an archived Text-only version of the thread. To view this thread in it's original format and/or if you want to reply to this thread please [click here]

 

Author Problems with ctype customization
Darko Miletic

2006-02-13, 7:05 pm

I stumbled upon problem with ctype customization.

I wanted to create iterator over string that will pass to the next
element not only with space but with any other character.

This is what I had that was working fine with std::istream_iterator and
std::string:

struct custom_ctype : public std::ctype<char>
{
custom_ctype(): std::ctype<char>(get_table()) {} //here reports
custom_ctype(const charT custom_delim): std::ctype<char>(get_table()) {
custom_ctype::custom_delimiter = custom_delim;
}
static std::ctype_base::mask const* get_table()
{
if (pptr__.get() == 0)
{
const charT delim = custom_delimiter;
pptr__.reset(new
std::ctype_base::mask[std::ctype<char>::table_size]);
std::fill_n(pptr__.get(), std::ctype<char>::table_size,
std::ctype_base::mask());
pptr__.get()[delim] = std::ctype_base::space;
}
return pptr__.get();
}
static charT custom_delimiter;
static std::auto_ptr<std::ctype_base::mask> pptr__;
private:
custom_ctype(const custom_ctype& rhs);
custom_ctype& operator=(const custom_ctype& rhs);
};

std::auto_ptr<std::ctype_base::mask> custom_ctype<char>::pptr__(NULL);
char custom_ctype<char>::custom_delimiter = ' ';


Then I wanted to switch to std::wstring and problems started.
I changed char to wchar_t but compiler complains with code like this:

error C2664: 'std::ctype<wchar_t>::ctype(size_t)' : cannot convert
parameter 1 from 'const std::ctype_base::mask *' to 'size_t'
This conversion requires a reinterpret_cast, a C-style cast or
function-style cast
c:\freerep\borland\bdsproj2mak_ms\Custom
Algorithm.h(8) : while
compiling class-template member function
'custom_ctype<charT>::custom_ctype(void)'
with
[

charT=std::basic_string<wchar_t,std::char_traits<wchar_t>,std::allocator<wchar_t>>::value_type
]
see reference to class template instantiation
'custom_ctype<charT>' being compiled
with
[

charT=std::basic_string<wchar_t,std::char_traits<wchar_t>,std::allocator<wchar_t>>::value_type
]

This is how I assign new ctype to wstringstream where bf is wstringstream:

bf.imbue(std::locale(std::locale(), new custom_ctype<wchar_t>()));

I use MSVC 7.1

Any idea?
Ulrich Eckhardt

2006-02-14, 4:00 am

Darko Miletic wrote:
> std::auto_ptr<std::ctype_base::mask> custom_ctype<char>::pptr__(NULL);


Identifiers like 'pptr__' are reserved to the implementation and not for
your use.

> error C2664: 'std::ctype<wchar_t>::ctype(size_t)' : cannot convert
> parameter 1 from 'const std::ctype_base::mask *' to 'size_t'


All facets have a ctor that takes a single argument, the reference counter.
You are trying to instantiate it using a ctype_base::mask pointer instead.
IOW, you simply don't have a matching type.

Other than that, please when providing code, provide the real code. Yours
was a mixture of a template and a non-templace ctype facet which surely
didn't compile.

Uli



Tom Widmer [VC++ MVP]

2006-02-14, 8:01 am

Darko Miletic wrote:
> I stumbled upon problem with ctype customization.
>
> I wanted to create iterator over string that will pass to the next
> element not only with space but with any other character.
>
> This is what I had that was working fine with std::istream_iterator and
> std::string:
>
> struct custom_ctype : public std::ctype<char>
> {


>
> Then I wanted to switch to std::wstring and problems started.


> Any idea?


ctype<char> is an explicit specialization of ctype<T> that has a special
constructor taking a table pointer (since a table big enough for char
isn't generally a big deal) - your code uses this constructor. For
ctype<wchar_t>, there is no such constructor (since wchar_t is 32 bits
on some common platforms, so the table would be huge). Instead, you need
to override the ctype virtual functions do_is. Something like this:

#include <vector>
#include <algorithm>
#include <iterator>
#include <locale>

template <class charT>
struct custom_ctype : public std::ctype<charT>
{
typedef std::ctype<charT> base;

custom_ctype(std::basic_string<charT> spaceChars)
:m_spaceChars(spaceChars)
{}

protected:
virtual bool do_is(base::mask m, charT c) const
{
base::mask M;
do_is(&c, &c + 1, &M);
return (M & m) == m; //check M has at least all bits of m set.
}

virtual const charT* do_is(const charT* low, const charT* high,
base::mask* vec) const
{
base::do_is(low, high, vec);
for (; low != high; ++low, ++vec)
{
if (m_spaceChars.find(*low) != std::basic_string<charT>::npos)
{
*vec |= ctype_base::space;
}
else
{
*vec |= ~ctype_base::space;
}
}
return high;
}

private:
std::basic_string<charT> m_spaceChars;
custom_ctype(const custom_ctype& rhs);
custom_ctype& operator=(const custom_ctype& rhs);
};

template<>
struct custom_ctype<char> : public std::ctype<char>
{
custom_ctype(std::string const& spaceChars):
std::ctype<char>(get_table(spaceChars), true) {}

static std::ctype_base::mask const* get_table(std::string const&
spaceChars)
{
std::ctype_base::mask* table = new
std::ctype_base::mask[std::ctype<char>::table_size];
std::transform(classic_table(),
classic_table() + std::ctype<char>::table_size,
table,
nospacer()
);
for (int i = 0; i != spaceChars.size(); ++i)
{
table[spaceChars[i]] |= std::ctype_base::space;
}
return table;
}
private:
struct nospacer
{
std::ctype_base::mask operator()(std::ctype_base::mask m) const
{
return m | ~ctype_base::space;
}
};

custom_ctype(const custom_ctype& rhs);
custom_ctype& operator=(const custom_ctype& rhs);
};

#include <iostream>
#include <sstream>

int main()
{
{
std::istringstream iss("hello.world,");
std::locale loc(std::locale(), new custom_ctype<char>(",."));
iss.imbue(loc);

std::string s, t;
iss >> s >> t;
std::cout << s << '\n' << t << std::endl;
}

{
std::wistringstream iss(L"hello.world,");
std::locale loc(std::locale(), new custom_ctype<wchar_t>(L",."));
iss.imbue(loc);

std::wstring s, t;
iss >> s >> t;
std::wcout << s << L'\n' << t << std::endl;
}
}

Your original version was not threadsafe, and creating a second facet
would have modified the first - creating a new table each time is very
unlikely to change the performance of a program in a noticeable way, and
if it does, the user should just reuse the same locale object.

Tom
Tom Widmer [VC++ MVP]

2006-02-14, 8:01 am

Tom Widmer [VC++ MVP] wrote:

Spot the deliberate mistakes:

> *vec |= ~ctype_base::space;


Should be:
*vec &= ~ctype_base::space;


> struct nospacer
> {
> std::ctype_base::mask operator()(std::ctype_base::mask m) const
> {
> return m | ~ctype_base::space;


Similarly, that should be:

return m & ~ctype_base::space;

Tom
Darko Miletic

2006-02-14, 8:01 am

Tom Widmer [VC++ MVP] wrote:
> Tom Widmer [VC++ MVP] wrote:
>
> Spot the deliberate mistakes:
>
>
> Should be:
> *vec &= ~ctype_base::space;


Thank you so much for explaining this to me.

Darko
Igor Tandetnik

2006-02-14, 8:01 am

"Ulrich Eckhardt" <eckhardt@satorlaser.com> wrote in message
news:44f8c3-7t7.ln1@satorlaser.homedns.org
> Darko Miletic wrote:
>
> Identifiers like 'pptr__' are reserved to the implementation and not
> for your use.


Not true. Identifiers with leading underscores are reserved, not those
with trailing underscores.
--
With best wishes,
Igor Tandetnik

With sufficient thrust, pigs fly just fine. However, this is not
necessarily a good idea. It is hard to be sure where they are going to
land, and it could be dangerous sitting under them as they fly
overhead. -- RFC 1925


Ulrich Eckhardt

2006-02-14, 7:06 pm

Igor Tandetnik wrote:
> "Ulrich Eckhardt" <eckhardt@satorlaser.com> wrote in message
> news:44f8c3-7t7.ln1@satorlaser.homedns.org
>
> Not true. Identifiers with leading underscores are reserved, not those
> with trailing underscores.


Is true. Those with two consecutive underscores are reserved and those
beginning with an underscore followed by a capital.

Anyhow, in this case the underscores for sure do not add anything to the
code, so it is questionable practice anyway.

Uli

Stephen Howe

2006-02-14, 7:06 pm

> Not true. Identifiers with leading underscores are reserved, not those
> with trailing underscores.


It is the number of consecutive underscores that matter. I see
[color=darkred]
17.4.3.1.2 Global names [lib.global.names]

1 Certain sets of names and function signatures are always reserved to the
implementation:
- Each name that contains a double underscore (_ _) or begins with an
underscore followed by an uppercase letter (2.11) is reserved to the
implementation for any use.
- Each name that begins with an underscore is reserved to the implementation
for use as a name in the
global namespace.165)[color=darkred]
[color=darkred]
17.4.3.1.3 External linkage [lib.extern.names]
3 Each name having two consecutive underscores (2.11) is reserved to the
implementation for use as a name with both extern "C" and extern "C++"
linkage.[color=darkred]

Stephen Howe


Tom Widmer [VC++ MVP]

2006-02-14, 7:06 pm

Darko Miletic wrote:
> Tom Widmer [VC++ MVP] wrote:
>
>
>
> Thank you so much for explaining this to me.


Do you mean my original post, or are you sarcastically chiding me for
not explaining why the |= needed to be changed to an &=, or are you
chiding me for providing code in my original post without sufficient
explanation? :)

Tom
Darko Miletic

2006-02-14, 7:06 pm

Tom Widmer [VC++ MVP] wrote:
> Darko Miletic wrote:
> Do you mean my original post, or are you sarcastically chiding me for
> not explaining why the |= needed to be changed to an &=, or are you
> chiding me for providing code in my original post without sufficient
> explanation? :)


I was reffering to the original post. I always welcome good piece of
help, because usualy people start bitching about underscores and other
less significant stuff that in fact does not provoke particular problem.

Thanks again.
Sponsored Links







Also available: Server administration forum archive | Web Design forum archive | Software forum archive | Hardware reviews archive

Copyright 2008 codecomments.com