Conversion: char * to char const *&

Nov 5, 2011 at 5:58pm
Given a char array "str" and a function "foo" which takes a 'char const *&' argument, is there any better/shorter alternative to the following?

foo( *const_cast< char const ** >( &str ) );

Nov 5, 2011 at 6:03pm
Not really, because what you're trying to do is unsafe and conceptually flawed.

Consider this:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
const char c[] = "This string cannot change!!!";

void foo(const char*& s)
{
  s = c;  // totally legal
}

int main()
{
  char* p;
  foo( p );  // assuming this would compile, which it wouldn't

  p[0] = 'D'; // YIKES!  we're modifying a const string!
}


This is something you probably shouldn't be trying to do.
Nov 5, 2011 at 6:04pm
I don't know what you mean, but that code looks like the wrong answer for almost any question.

Can you post the code in question please.
Nov 5, 2011 at 6:22pm
@Disch
Thanks, I see now the problem.

@kbw
Example:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
void detectString( char const *& a_begin )
// The function only modifies the pointer, that's why I put const here.
{
    while ( *a_begin == ' ' )
        ++a_begin;
}

int main()
{
    char text[] = "       bello";
    char * begin = text;
    detectString( begin );
    *begin = 'h';
    // 'begin' now points to "hello".
}
Last edited on Nov 5, 2011 at 6:26pm
Nov 5, 2011 at 6:34pm
Mmm, that's a novel way of parsing.

You'll need to declare begin and end as const char *.

In main(), you have a mutable array called text. You have two mutable pointers into it called begin and end.

You declare a function detectString that takes two pointers to identify the first string in a space delimited string. But the pointers are const, they can't change the content of the array. That's fine, but you need to pass const pointers to the function.

You can't go from a const pointer to a non-const pointer without overriding the compiler (with a cast). In C++, if you find yourself using cast, you really should pause for thought.
Nov 5, 2011 at 6:47pm
You'll need to declare begin and end as const char *.

But I need them to be of type char * so I can modify the detected string later.
(Still this is only an example, not my actual code)

Only thing I could do is:
1
2
3
4
char * begin = text;
char const * begin_ = begin;
detectString( begin_ );
begin = begin_;


...which is bit longer than:
1
2
char * begin = text;
detectString( *const_cast< char const ** >( &begin ) );


I wonder if there is a better way.
Nov 5, 2011 at 11:28pm
You can change or overload decectString to taks char*& rather than const char*&. You really shouldn't be trying to cast here.

BTW, you use const_cast to remove const or volatile. You need static_cast.
Nov 6, 2011 at 11:37am
BTW, you use const_cast to remove const or volatile. You need static_cast.

How can static_cast remove const and where am I removing const?
I guess you mean the line in which I'm adding const. But this doesn't work either with static_cast.

You can change or overload decectString to taks char*& rather than const char*&.

That's a good idea, thanks! As I want the function to be inlined anyway I could use something like that:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
template< typename T >
void detectString( T *& a_begin )
{
    static_cast< char const * >( a_begin );  // Static assertion (T is char or char const).
    while ( *a_begin == ' ' )
        ++a_begin;
}

int main()
{
    char text[] = "     asdf";
    char * begin = text;
    detectString( begin );
}


Is it valid to (mis)use static_cast for that purpose?
Nov 6, 2011 at 2:58pm
static_cast invokes C++'s rules to convert from one type to the next. I can involve chaning the representation; for example converting int to double. I will also invoke user defined conversions on classes where necessary.

const_cast removes logical constness from an identifier.

reinterpret_cast reinterprets a bit pattern as another type.

dynamic_cast uses RTTI to determine if a type safe downcast is possible and applies it if it can.

Whenever you use a cast, you're overriding the type safe language's rules and you're taking matters into your own hands. It is possible to abuse all the language's type safety overrides. And unless you're sure about what you're doing, that's usually the case.

In C, it's necessary to cast all the time. That is not true in C++. Whenever you cast in C++, you really need to stop and think why you're in that situation.
Nov 6, 2011 at 3:22pm
So how would you replace the line
static_cast< char const * >( a_begin );
in my above example?

Btw: To avoid compiler warnings or errors I actually need to use static_cast quite often. And I think that's inevitable:

- When checking if an index (int) is in range (uint)
- When assigning pointer subtractions to ints (x64)
- When assigning size_t values ( e.g. strlen ) to ints or uints (x64)
- When resolving ambiguous calls to overloaded functions
Nov 6, 2011 at 3:29pm
To avoid compiler warnings or errors I actually need to use static_cast quite often.
That's not good at all.

- When checking if an index (int) is in range (uint)
You've just opened yourself up to a very subtle error.

- When assigning pointer subtractions to ints (x64)
Pointers arn't ints. That's why C was written in the first place, otherwise we'd still be using B.

- When assigning size_t values ( e.g. strlen ) to ints or uints (x64)
A size_t isn't an int.

- When resolving ambiguous calls to overloaded functions
If you were using the correct types, you wouldn't need to correct youself later on. Remember the compiler is trying to help you; you should accept that help.
Last edited on Nov 6, 2011 at 3:38pm
Nov 6, 2011 at 3:50pm
Poiners arn't ints. That's why C was written in the first place, otherwise we'd still be using B.

So you'd never write something like that:

unsigned int size = pEnd - pBegin;

?

A size_t isn't an int.

Unfortunately there ain't no strlen that returns an int, so I have to cast.

If you were using the correct types

Sometimes I have to use 0, which could be an int, a char, a pointer.

I'm not going to write every time such:

int const nullint = 0;

Before calling the function.

Last edited on Nov 6, 2011 at 3:53pm
Nov 6, 2011 at 7:29pm
All pointers are an address in memory, which looks like a integer of some form. However they are not integers, and do have precise ranges that you can play with. The wrong math on a pointer could do some harm you didn't intend to do and most likely cause an exception fault or page fault in modern operating systems. Doing math on a pointer should be done with extreme caution.

size_t, if I remember, is a structure and not an Integer because of how it is used an many of the objects in STL. size_t for char is 1 but size_t for a wide char is 2 and both can be used in a string but they are two different types of strings at that moment.

Zeroing a pointer is casting it to NULL of some form, however in modern c++ isn't wise to set to zero. Use the keyword NULL for all pointer types, which is a more accurate description. Memory location of 0 does exist in most computers, where NULL is not defined as 0. In older c's we could get away with that but in modern ones they have removed that ambiguity that was there.
Nov 7, 2011 at 1:11am
So you'd never write something like that:
 
unsigned int size = pEnd - pBegin;

There's a standard type for pointer differences, ptrdiff_t

Unfortunately there ain't no strlen that returns an int, so I have to cast.
strlen returns a size_t, which is often unsigned.

Sometimes I have to use 0, which could be an int, a char, a pointer.
It's common to use 0 to indicate a null pointer, however 0 is an int that's converted to some other type depending on the context. That's been fixed with the introduction of nullptr

I'm not going to write every time such: ...
I agree, that'd be wrong.
Nov 7, 2011 at 5:58am
strlen returns a size_t, which is often unsigned.

Actually size_t is typedefed an uint on the compiler I use. But I still need to static_cast it when assigning it to a uint variable to avoid a warning like "this could be a potentil data loss if you'd compile for x64" which I might be doing some day.

For the same reason (possible data loss if compiling for x64) I cannot write something like

myint += ( pEnd - pBegin )

without casting, wich is a perfectly valid operation, isn't it?

There's a standard type for pointer differences, ptrdiff_t

Interesting, didn't know about that.

@Azagaros
I think you're confusing size_t with sizeof. Also I've been told to NOT use 'NULL' in C++.
Topic archived. No new replies allowed.