3

Writing a C++ wrapper for a C library. In the library there is a function which accepts a buffer as unsigned char *. Since working with C++ I store my real buffer in std::vector<char>, moreover, in the wrapper class I also added const, because the buffer in question is an input buffer and it wont get modified. But my code does not compile, demo:

#include <stdio.h>
#include <vector>

void x(unsigned char *s)
{
    printf("%s\n", s);
}

int main()
{
    const std::vector<char> s(10);

    x(reinterpret_cast<unsigned char *>(s.data())); // FAILS
}

What is wrong with id? If I remove the consts from const std::vector<char> s(10); it works, but I would prefer it that way.

Rama
  • 3,222
  • 2
  • 11
  • 26
J. Doe
  • 73
  • 9
  • This would be a `const_cast` not a `reinterpret_cast`. But why `x` doesn't take a `const unsigned char*`? – overseas Mar 13 '17 at 19:22
  • Because we don't write unsigned char *s = "hello world" either. We write char * ;) – J. Doe Mar 13 '17 at 19:29
  • @J.Doe Despite the downvotes, I'm pretty sure I've answered your question. I also added some details considering the logic behind the conception. – Telokis Mar 13 '17 at 19:31
  • Yes, I am grateful. And will accept as solution as soon as the button for it appears to do it so. – J. Doe Mar 13 '17 at 19:54
  • The two issues here are: 1) `x()` isn't `const` correct; it should take `const unsigned char*`. 2) `reinterpret_cast` can't remove `const`-ness; it can cast a `const char*` to `const unsigned char*`, but not to `unsigned char*`. No need to remove `s`'s `const`-ness if you can modify `x()`. – Justin Time - Reinstate Monica Mar 13 '17 at 21:04
  • Unfortunately x() was defined as is in the C library's header I am making a wrapper for (cannot touch that). – J. Doe Mar 13 '17 at 21:22

3 Answers3

7

You have to do const_cast to remove constness and then reinterpret_cast to cast char * into unsigned char *

Try this: x(reinterpret_cast<unsigned char*>(const_cast<char *>(s.data())));

Note: But also remember that const_cast is safe only if you're casting a variable that was originally non-const. See this answer.

Community
  • 1
  • 1
JustRufus
  • 492
  • 1
  • 5
  • 10
1

Your error is because you declare your std::vector as const.

If you drop the constness, it works properly. (As seen here)

Anyway, you should ask yourself if you really need to store chars or unsigned chars in your array.

If you want to keep the const, you would have to use a const_cast but your line would become pretty long :

x(const_cast<unsigned char*>(reinterpret_cast<const unsigned char *>(s.data())));

If you have access to the library, it would have been better to mark the buffer as const if it isn't changed. But, since it isn't const, you can't be sure it won't be modifier by the function and, because of that, you shouldn't keep your std::vector as a const.

Telokis
  • 3,399
  • 14
  • 36
  • The OP specifically mentions this, and says that they would rather not drop the `const`. – Rakete1111 Mar 13 '17 at 19:26
  • Yes but I don't type instantly, I'm sorry. – Telokis Mar 13 '17 at 19:28
  • Jesus. Very well done Mr. Bjourstroup. How come you did not think of this mess when you invented C++?? I think I will stick with C style (unsigned char *) casting. – J. Doe Mar 13 '17 at 19:31
  • Well, I really think the issue is the constness of the parameter of the C-function. If it isn't `const`, you can't be sure it won't touch your data and, because of that, your `std::vector` should not be `const` either. – Telokis Mar 13 '17 at 19:32
  • By the way I wanted it to be const, also because this is what I have observed. All string arguments are also passed as const string ref& and I thought it would be a great idea to follow this on vector (when I am not modifying it). – J. Doe Mar 13 '17 at 20:01
  • @J.Doe, Unsafe and unusual conversions are messy for a reason. Anyone who sees this line instantly knows there's some shady stuff going on, which is pretty apt given that you're casting away constness and switching signedness. – chris Mar 13 '17 at 20:10
  • "shady stuff" well put – J. Doe Mar 13 '17 at 20:22
-1

This is how I fixed the same problem during my cross platform implementations. Above methods worked fine on Windows and Linux (Redhat) but not on Solaris 10 and IBM AIX 7.

Solaris 10 gives following error with above suggested methods. 'nofieldident: data is not a member of const std::vector<unsigned char>'

Here is a way to work with the 'const'ness of vector data, which will work across Windows, Linux, Solaris

#include <iostream>
#include <vector>

using namespace std;

/// Data Interface, which is visible to the outside
class IDataInterface {
public:
        // Get the underlying data as a const data pointer
        virtual const unsigned char* Data(size_t& nSize) = 0;
};

/// Implemetation of the IDataInterface
class CData : public IDataInterface {
public:
        // Constructor
        CData(vector<unsigned char> data);

        // Overriden function of the interface
        const unsigned char* Data(size_t& nSize);

private:
        /// Actual data member
        vector<unsigned char> m_vData;
};

/// Constructor implementation
CData::CData(vector<unsigned char> vData) {
        // resize to the input data size
        m_vData.resize(vData.size());

        // Copy the data
        memcpy(&m_vData[0], &vData[0], vData.size());
}

/// Implementation of the data function
const unsigned char* CData::Data(size_t& nSize /**< Size of data returned */) {
        /*
         * Following four methods worked fine on Windows, RedHat, but Failed on Solaris 10 and IBM AIX
         */
        // return &m_vData[0];
        // return m_vData.data();
        // return const_cast<unsigned char*>(reinterpret_cast<const unsigned char *>(m_vData.data()));
        // return reinterpret_cast<unsigned char*>(const_cast<unsigned char *>(m_vData.data()));
        
        /* This was tested on following and works fine.
         * Windows (Tested on 2008 to 2016, 32/64 bit, R2 versions)
         * RedHat 5&7 (32/64 bit)
         * Solaris 10 (32/64 bit)
         * IBM AIX 7.10
         *
         */
        return &m_vData.front();  --> This works on windows, redhat, solaris 10, aix
}

/// Main class
int main() {
        // Vector of data
        vector<unsigned char> vData;

        // Add data onto the vector
        vData.push_back('a');
        vData.push_back('b');
        vData.push_back('c');

        // Create an instance from the vector of data given
        CData oData(vData);

        // Get the data from the instance
        size_t nSize(0);
        cout << "Data:" << oData.Data(nSize) << endl;

        return 0;
}

Output:
Data:abc
SajithP
  • 592
  • 8
  • 19
  • Why down vote? :( This is tested code to solve const issues on windows 2008, 2008R2, 2010, 2010R2, 2016,Linux RHEL5 and 7, Soloris 8 and 10, IBM AIX 7 and on 32 and 64 bit platforms. – SajithP Aug 05 '21 at 01:16