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.
|
|
|
|
|