0
int FunctionName(const char *pValueName, const char *pValueData, long iMaxValueSize)
{
  char *pDataToStore = const_cast<char *>(pValueData);
  int iActualSiz = ProcessData(pDataToStore, iMaxValueSize);
...
...
}

In the upper code snippet ProcessData() function modifies the char*, which it receives as parameter. Now even after assigning pValueData into pDataToStore, after ProcessData() get executed, value of pValueData is being same as pDataToStore.

My aim is to keep intact value of pValueData which is being passed as const char*

Yu Hao
  • 119,891
  • 44
  • 235
  • 294
hypheni
  • 756
  • 3
  • 17
  • 37

4 Answers4

2

My aim is to keep intact value of pValueData which is being passed as const char*

That's impossible. Passing via const means it cannot be modified, except when it was originally not constant.

Example:

char *ptr1 = new char[100]; // not const
char *ptr2 = new char[100]; // not const
int i = FunctionName(ptr1, ptr2, 123);

In this case, you could technically keep the const_cast. But what for? Just change your function parameters to take char *:

int FunctionName(char *pValueName, char *pValueData, long iMaxValueSize)
{
  int iActualSiz = ProcessData(pValueData, iMaxValueSize);
  // ...
}

However, you most likely want to be able to pass constant strings. For example string literals:

int i = FunctionName("name", "data", 123);

String literals are unmodifiable and thus require your function to take char const *. A later attempt to modify them causes undefined behaviour.


As you can see, the error is in the general architecture and code logic. You want to modify something and at the same time you do not want to allow to modify it.

The question is: What happens with your pDataToStore when ProcessData is done with it? Does the caller of FunctionName need to be aware of the modifications? Or is it just internal business of FunctionName?

If it's just internal business of FunctionName, then you can keep its signature intact and have ProcessData modify a copy of the passed data. Here is a simplified (not exception-safe, no error checks) example:

int FunctionName(const char *pValueName, const char *pValueData, long iMaxValueSize)
{
   char *copy = new char[strlen(pValueData) + 1];
   strcpy(copy, pValueData):
   int iActualSiz = ProcessData(copy, iMaxValueSize);

   // ...

   delete[] copy;
}

The nice thing is that you can now massively improve the interface of FunctionName by hiding all the low-level pointer business. In fact, why use so many pointers at all when C++ standard classes can do all the work for you?

int FunctionName(std::string const &valueName, std::string const &valueData, long maxValueSize)
{
   std::vector<char> copy(valueData.begin(), valueData.end());
   int actualSize = ProcessData(&copy[0], maxValueSize);

   // ...
   // no more delete[] needed here
}

The std::vector<char> automatically allocates enough memory to hold a copy of valueData, and performs the copy. It fully automatically frees the memory when it is no longer needed, even if exceptions are thrown. And &copy[0] (which in C++11 can be written as copy.data()) is guaranteed to yield a pointer to the internally used data, so that low-level C functions can modify the vector's elements.

(I've also taken the chance to remove the Microsoft-style Hungarian Notation. It's a failed experiment from the 90s, and you've even used it incorrectly, supposing that a leading i is supposed to indicate an int.)


The bottom line is really:

If you need a const_cast anywhere in your code to make it compile, then somewhere else there is at least either one const missing or one too much. A const_cast always makes up for a mistake in another piece of code. It is always a workaround and never a solution designed up front.

Christian Hackl
  • 27,051
  • 3
  • 32
  • 62
  • +1 That's excellent ! Same conclusions as in my answer, but I really like your approach consisting of just changing the signature to string. However there is a small issue, as we are not sure that the char* data is really a null terminated string. – Christophe Aug 23 '14 at 10:57
  • Nice detailed answer, I would say differently in bottom line : `const_cast` should only be used when using legacy code from correctly written code. – Serge Ballesta Aug 23 '14 at 11:29
  • @SergeBallesta: But is this not more or less what my own bottom line says? :) Perhaps one should make a distinction between "legacy code" (which may have been great by the standards of the time it was written) and "bad code" (which was already bad by its creation time). – Christian Hackl Aug 23 '14 at 12:05
  • @Christophe: you are right. If the string is not null-terminated, then the size information must come from somewhere else. Perhaps that's somehow the meaning of the `long` parameter? Or the size information comes from a completely different place anywhere. `ProcessData` may assert a size known at compile time. Or the size may be a global or static variable. We really cannot know with this simple piece of code; so I assumed null-terminated strings. – Christian Hackl Aug 23 '14 at 12:11
  • @Christophe: Vector solution is a sureshot winner for c++. Thanks for your support. – hypheni Aug 26 '14 at 05:21
0

Well I have solved the issue by creating the heap memory.

char *pDataToStore = new char[iMaxValueSize];
memcpy(pDataToStore, pValueData, iMaxValueSize*sizeof(char));
int iActualSiz = ProcessData(pDataToStore, iMaxValueSize);
...
....
delete []pDataToStore;
hypheni
  • 756
  • 3
  • 17
  • 37
  • 1
    That's a really bad use of `memcpy` (it's rarely useful in C++ anyway). Use `std::copy` or a `std::vector`; see my answer. – Christian Hackl Aug 23 '14 at 10:47
  • `iMaxValueSize` is defined as long whereas `new[]` and `memcpy()` can handle only `size_t`. @ChristianHackl you are right about the style, however from the performance performance point of view it's a valid approach. – Christophe Aug 23 '14 at 11:30
  • @Christophe: Not true. See http://stackoverflow.com/questions/4707012/c-memcpy-vs-stdcopy, http://stackoverflow.com/questions/7285952/efficiency-of-stdcopy-vs-memcpy and many related questions about memcpy vs std::copy. – Christian Hackl Aug 23 '14 at 12:03
  • @Christophe `size_t` can hold the maximum number of bytes for a single allocation – M.M Aug 23 '14 at 12:45
  • @MattMcNabb that's exactly what I wanted to highlight. If iMaxValueSize > SIZE_MAX there is a problem for the allocation here. – Christophe Aug 23 '14 at 12:59
  • 1
    @Christophe add in a check `if ( iMaxValueSize < 0 || iMaxValueSize > SIZE_MAX ) /* abort */` – M.M Aug 23 '14 at 13:01
0

You have to make a difference between a const qualified type and a const qualified object.

The standard states in section 7.1.6.1: cv-qualifiers: (cv = const or volatile)

A pointer or reference to a cv-qualified type need not actually point or refer to a cv-qualified object, but it is treated as if it does; a const-qualified access path cannot be used to modify an object even if the object referenced is a non-const object and can be modified through some other access path.

If your pointer points to a non const object, the casting away will enable you to modifiy the objet, but as someone told, you are lying to the user of your function.

It your pointer points to a real const object (i.e. in const protected memory), the compiler will compile your code, but you might have a segmentation fault, typical for undefined behaviour.

Here an example, using the fact that "Ordinary string literal (...) has type “array of n const char”, where n is the size of the string (...)" (see standard, section 2.14.5):

char *my_realconst = "This is a real constant string";    // pointer does not claim that it points to const object 

(*my_realconst)++;  // Try to increment the first letter, will compile but will not run properly !! 

So if your function ProcessData() is legacy code that is only reading the data but has forgotten to mention a const in the parameter list, your cast-away will work. If your function is however altering the data, it might work or it might fail, depending how the data poitned to was created !

So try to avoid casting const away if you are not 100% sure of what the effects will be ! Better clone your object the hard way creating a temporary object and copying the content.

Christophe
  • 68,716
  • 7
  • 72
  • 138
0

I propose you a small template to handle these kind of issues easily:

template <typename T>
class Buffer { 
    size_t sz;      // size
    T* addr;        // pointed
public: 
    Buffer(const T*source, size_t l) : sz(l), addr(new T[l]) { std::copy(source, source + l, addr);  }  // allocate and copy
    ~Buffer() { delete[]addr; }   // destroy memory 
    operator T* () { return addr; }  // convert to pointer
};

You may use your existing code almost as is:

Buffer<char> pDataToStore(pValueData, iMaxValueSize);  // create the automatic buffer
int iActualSiz = ProcessData(pDataToStore, iMaxValueSize);  // automatic use of pointer to buffer
cout << "modified copy: " << pDataToStore << endl;
cout << "original:      " << pValueData << endl;

The buffer will be automatically released once pDataToStore is no longer in scope.

If you have similar issues with wchar_t buffers or anything else, it will work as well.

For explanations on the evil of casting away const, see my other answer

Christophe
  • 68,716
  • 7
  • 72
  • 138