Trying to work by the design guidelines for GIL, I use bits__ for my channel data types. I often have external data I'm wrapping into GIL image views. However, even using the bits__ types for data pointers, I have to add in a reinterpret_cast before I can create my image views. Take the following code
int width = 3;
int height = 2;
boost::gil::bits8 data8[] = {0, 1, 100, 200, 50, 51};
boost::gil::bits8* pBits8 = data8;
boost::gil::gray8_ptr_t pGray8 = pBits8;
boost::gil::gray8_view_t v = interleaved_view(width, height, pGray8, width * sizeof(boost::gil::bits8));
Results in the error on line 6 "error C2440: 'initializing' : cannot convert from 'boost::gil::bits8 *' to 'boost::gil::gray8_ptr_t'
1> Types pointed to are unrelated; conversion requires reinterpret_cast, C-style cast or function-style cast"
Delving into the source code as much as I can, it appears these types really are unreleated. bits8 is just unsigned char, but gray8_ptr_t is a pointer to a struct pixel<bits8,gray_layout_t>. The only element of this struct is a single bits8, so a reinterpret_cast looks safe. It also works fine for the tests I've thrown at it.
However, I wrap external data into image views quite often, and having a reinterpret_cast in every place feels problematic. Is there a safer way of constructing a pixel pointer for use in GIL?
Current workaround:
template<class Dest, class Src>
Dest gil_safe_ptr_cast(Src src)
{
// this cast is unsafe, use reinterpret_cast
BOOST_STATIC_ASSERT(false);
}
template<> boost::gil::gray8_ptr_t gil_safe_ptr_cast(boost::gil::bits8* pBits8)
{
return reinterpret_cast<boost::gil::gray8_ptr_t>(pBits8);
}
boost::gil::bits8* pBits8 = data8;
boost::gil::gray8_ptr_t pGray8 = gil_safe_ptr_cast<boost::gil::gray8_ptr_t>(pBits8); // works
boost::gil::bits16* pBits16 = NULL;
boost::gil::gray8_ptr_t pGray82 = gil_safe_ptr_cast<boost::gil::gray8_ptr_t>(pBits16); // compile error as expected
To convert from bits8* to gray8_ptr_t, create a struct pixel and provide the bits8 to the constructor:
gray8_ptr_t convert_gray8_ptr_t(bits8* src) {
return new struct pixel<bits8,gray_layout_t>(*src);
}
To convert back, use the struct's conversion operator:
bits8* convert_bits8(gray8_ptr_t src) {
bits8* result = new bits8;
*result = (bits8) *src;
return result;
}
Of course both of these functions allocate memory and are probably unnecessary as functions (better as inline code).
template<class Dest, class Src>
Dest gil_safe_ptr_cast(Src src)
{
// this cast is unsafe, use reinterpret_cast
BOOST_STATIC_ASSERT(false);
}
template<> boost::gil::gray8_ptr_t gil_safe_ptr_cast(boost::gil::bits8* pBits8)
{
return reinterpret_cast<boost::gil::gray8_ptr_t>(pBits8);
}
boost::gil::bits8* pBits8 = data8;
boost::gil::gray8_ptr_t pGray8 = gil_safe_ptr_cast<boost::gil::gray8_ptr_t>(pBits8); // works
boost::gil::bits16* pBits16 = NULL;
boost::gil::gray8_ptr_t pGray82 = gil_safe_ptr_cast<boost::gil::gray8_ptr_t>(pBits16); // compile error as expected
Related
class mapInfo
{
public:
mapInfo();
~mapInfo();
public:
int dataType_m;
private:
int *frequency;
};
//constructor is defined here.
mapInfo::mapInfo() :
dataType_m(0),
frequency(NULL)
{
}
//destructor is defined here
mapInfo::~mapInfo()
{
free(frequency);
frequency = NULL;
}
Result_t Maps::add(mapInfo &mapInfo_r)
{
if (maps_mp == NULL)
{
numMaps_m = 1;
maps_mp = (mapInfo *) calloc(1, sizeof(mapInfo));
}
else
{
numMaps_m++;
maps_mp = (mapInfo *) realloc(maps_mp, numMaps_m*sizeof(mapInfo));
}
maps_mp[numMaps_m-1] = mapInfo_r; // Default copy constructor
return 1;
}
While compiling with gcc8, getting the following compilation error. It looks like defining the destructor like above giving the compilation error for gcc8.
How to resolve this?
error: 'void* realloc(void*, size_t)' moving an object of non-trivially copyable type 'class xyyz::mapInfo'; use 'new' and 'delete' instead [-Werror=class-memaccess].
That’s simply not proper C++. Rewrite your code as follows (I’m guessing here with regards to the type of frequency, but definitely don’t use free on it):
#include <vector>
class map_info
{
public:
map_info();
private:
int data_type;
std::vector<int> frequency;
};
std::vector<map_info> maps_mp;
map_info::map_info() : data_type(0), frequency() {}
// …
void maps::add(map_info& map_info)
{
maps_mp.push_back(map_info);
}
maps_mp = (mapInfo *) realloc(maps_mp, numMaps_m*sizeof(mapInfo));
This is not sensible. You can't just move an object from one aree of memory to another if that object is non-trivial.
For example, consider a string object that keeps a pointer to the string. It could look like this:
class MyString
{
char* inner_ptr;
char buf[64];
...
};
And it might have a constructor like this:
MyString::MyString (const char* j)
{
if (strlen(j) < 64)
inner_ptr = buf;
else
inner_ptr = malloc (strlen(j) + 1);
strcpy(inner_ptr, j);
}
And a destructor like this:
MyString::~MyString()
{
if (buf != inner_ptr)
free (inner_ptr);
}
Now, think about what happens if you call relloc on an array of these. The short strings will still have their inner_ptrs pointing to the old object's buffer, which you just deallocated.
The error message explains this issue reasonable well. It is simply not legal to use realloc to move a non-trivial object. You have to construct a new object because the object needs a chance to handle the change in its address.
With the following snippet:
int n = 11;
int* c = &n;
void** v = &c;
I receive the following error in visual studio:
the value of type int** cannot be used to initialize an entity of type void **.
This works fine:
int n = 11;
int* c = &n;
void* v = c;
But this code snippet is for a larger problem in someone's library.
What am I doing wrong with casting a variable to void**?
Complete Example
Using the caen digitizer library the way they try to collect data from the peripheral device has this prototype:
/******************************************************************************
* X742_DecodeEvent(char *evtPtr, void **Evt)
* Decodes a specified event stored in the acquisition buffer writing data in Evt memory
* Once used the Evt memory MUST be deallocated by the caller!
*
* [IN] EventPtr : pointer to the requested event in the acquisition buffer (MUST BE NULL)
* [OUT] Evt : event structure with the requested event data
* : return 0 = Success;
******************************************************************************/
int32_t X742_DecodeEvent(char *evtPtr, void **Evt);
And this is the implementation:
int32_t X742_DecodeEvent(char *evtPtr, void **Evt) {
CAEN_DGTZ_X742_EVENT_t *Event;
uint32_t *buffer;
char chanMask;
uint32_t j,g,size;
uint32_t *pbuffer;
uint32_t eventSize;
int evtSize,h;
evtSize = *(long *)evtPtr & 0x0FFFFFFF;
chanMask = *(long *)(evtPtr+4) & 0x0000000F;
evtPtr += EVENT_HEADER_SIZE;
buffer = (uint32_t *) evtPtr;
pbuffer = (uint32_t *) evtPtr;
eventSize = (evtSize * 4) - EVENT_HEADER_SIZE;
if (eventSize == 0) return -1;
Event = (CAEN_DGTZ_X742_EVENT_t *) malloc(sizeof(CAEN_DGTZ_X742_EVENT_t));
if (Event == NULL) return -1;
memset( Event, 0, sizeof(CAEN_DGTZ_X742_EVENT_t));
for (g=0; g<X742_MAX_GROUPS; g++) {
if ((chanMask >> g) & 0x1) {
for (j=0; j<MAX_X742_CHANNEL_SIZE; j++) {
Event->DataGroup[g].DataChannel[j]= malloc(X742_FIXED_SIZE * sizeof (float));
if (Event->DataGroup[g].DataChannel[j] == NULL) {
for (h=j-1;h>-1;h++) free(Event->DataGroup[g].DataChannel[h]);
return -1;
}
}
size=V1742UnpackEventGroup(g,pbuffer,&(Event->DataGroup[g]));
pbuffer+=size;
Event->GrPresent[g] = 1;
}
else {
Event->GrPresent[g] = 0;
for (j=0; j<MAX_X742_CHANNEL_SIZE; j++) {
Event->DataGroup[g].DataChannel[j] = NULL;
}
}
}
*Evt = Event;
return 0;
}
I use this by:
CAEN_DGTZ_X742_EVENT_t* Evt = NULL; // Creating my event pointer
//Doing some config of the device
X742_DecodeEvent(evtptr, &Evt); //Decode the event data for me to read (Throws error)
Hope this gives some context.
void** means a pointer to a void* object. But there is no void* object in that code to point at! void** does NOT mean "a pointer to any kind of pointer", so please avoid using it as such. If you have a pointer to something which might be an int*, might be a double*, or etc., void* is a better type than void**. Even better would be a template or std::variant or std::any.
But if you have to use a library that is using void** to mean "a pointer to a pointer to a type unknown at compile time" or something like that, you might need to create a void* pointer to work with, or might need to add in casts to get around the fact that the compiler doesn't like this conversion (for good reason). The problem is, there are at least two reasonable ways to do this! (They will end up doing exactly the same thing on many common computer architectures, but this is not guaranteed.)
// LibraryFunc1 takes a void** argument that somehow means an int* pointer.
// But which call is correct?
int* data_in = generate_data();
LibraryFunc1(reinterpret_cast<void**>(&data_in)); // ?
void* p1 = data_in;
LibraryFunc1(&p1); // ?
// LibraryFunc2 returns a void** argument that somehow means an int* pointer.
void** p2 = LibraryFunc2();
int* data_out_1 = static_cast<int*>(*p2); // ?
int* data_out_2 = *reinterpret_cast<int**>(p2); // ?
Based on the function definition shown, the safe usage is unfortunately:
void* tmpEvt;
X742_DecodeEvent(evtptr, &tmpEvt);
auto* Evt = static_cast<CAEN_DGTZ_X742_EVENT_t*>(tmpEvt);
since the library function assumes at *Evt = Event; that *Evt is actually a void* object it can modify. It may usually work to do the simpler thing instead:
CAEN_DGTZ_X742_EVENT_t* Evt = NULL;
X742_DecodeEvent(evtptr, reinterpret_cast<void**>(&Evt));
but this is undefined behavior by the C++ Standard, and might do the wrong thing on some architectures.
You could make the correct way easier by wrapping it in a function:
inline CAEN_DGTZ_X742_EVENT_t* Get_X742_DecodeEvent(char* evtPtr)
{
void* tmpEvt;
X742_DecodeEvent(evtPtr, &tmpEvt);
return static_cast<CAEN_DGTZ_X742_EVENT_t*>(tmpEvt);
}
What am I doing wrong with casting a variable to void**?
There is no meaningful way to convert int** to void**, so what you're trying to do is wrong.
What you may do is
int n = 11;
void* c = &n;
void** v = &c;
But without a complete example, it is not possible to say whether applies to your problem.
That's simply how the language works.
void * pointers get special treatment: a pointer to an arbitrary type can be converted to a pointer to void (as long as doing so doesn't remove cv-qualifiers from the pointer).
void ** gets none of that special treatment. It's just a regular pointer type, like int **.
int32_t X742_DecodeEvent(char *evtPtr, void **Evt)
Since you want to pass CAEN_DGTZ_X742_EVENT_t ** to your function, you should change the parameter type accordingly: CAEN_DGTZ_X742_EVENT_t **Evt.
In comments you were suggested to use void ** v = (void**)&c;.
While you could probably make it work in practice, strictly speaking any access to *v would violate strict aliasing and cause undefined behavior. I wouldn't use that solution.
Lets have following function:
int encode(uint8b *dest, MyType srcType, const void *src)
{
uint32b value = 0;
uint64b value64 = 0;
switch (srcType)
{
case MyType_Usint: value = (uint32b)*(uint8b*)src; break;
case MyType_Uint: value = (uint32b)*(uint16b*)src; break;
case MyType_Udint: value = *(uint32b*)src; break;
case MyType_Ulint: value64 = *(uint64b*)src; break;
}
// now encode value to dest
}
I have passed wrongly aligned uint8b Data[sizeof(uint64b)]as src, I will fix the alignment.
But I received a demand to cast Data when calling the function to proper type, i.e. encode(dest, MyType_Uint, (uint16b*)Data), which I think would cause in some more annoying unnecessary switches.
It's working even with the wrong alignment on platforms accessible for me, but I'm not sure, how it affects the other.
Would such cast fix alignment and/or endianess?
And yes, I really need the void* parameter.
encode(dest, MyType_Uint, (uint16b*)Data)
Would such cast fix alignment and/or endianess?
Such cast does not fix alignment nor endiannes.
The way to call that function without undefined behaviour is following:
uint16b u = some_value;
encode(dest, MyType_Uint, &u);
uint64b ul = some_other_value;
encode(dest, MyType_Ulint, &ul);
Does such cast actually do anything?
Such cast changes the type of the expression. In this case, the C style explicit conversion does reinterpret cast.
The converted pointer can be used only in limited ways. You can convert it back to the original type (uint8b* in your case). Indirection through the pointer is UB in most cases, with few exceptions, which include pointer-interconvertible objects, as well as using a pointer to a narrow character type as the result of the conversion. There is no exception that would apply to your example, so it would have UB.
Note that for some pointers, the C style explicit conversion does static cast, rather than reinterpret cast. For example, when the pointers are to classes in the same inheritance hierarchy. This is why C style casts are to be avoided: Use the cast that you intend to use.
Casting a pointer never fix alignment nor endianness. It just re-interpret the address as pointing to a different type, and standard does not allow to de-reference it unless an object of the appropriate type lies at that address.
The conformant way to use a possibly misaligned representation is to use memcpy:
int encode(uint8b *dest, MyType srcType, const void *src)
{
uint32b value = 0;
uint64b value64 = 0;
switch (srcType)
{
// any object can be accessed through a char pointer
case MyType_Usint: uint8b tmp8 = *(uint8b*)src; value = tmp8; break;
// use a temporary for 16 bits type (endianness question)
case MyType_Uint: uint16b tmp16; memcpy(&tmp16, src, sizeof(tmp16));
value = tmp16; break;
// directly memcpy into the value when size is the same
case MyType_Udint: memcpy(&value, src, sizeof(value)); break;
case MyType_Ulint: memcpy(&value64, src, sizeof(value64)); break;
}
// now encode value to dest
}
Intel type (80x86 to core) are known to be tolerant to misalignment, but other processors are not and raise an error if you attempt a misaligned access.
This code is unlikely to work on platform that can't do unaligned memory access (like ARM9/ARM64 etc...).
This is because when you do value64 = *(uint64b*)src you expect the CPU to access a 8 bytes word (and as such it must be aligned to 64bit address) and the signature of the function does not guaranty this.
Typically, on such platform, calling:
char s;
int a = encode(dest, MyType_Ulint, &s);
will compile, and will crash at runtime (but will run ok on x86/amd64 system, yet with undefined behavior).
If you want something portable, you should do something like this:
enum MyType
{
MyType_Usint,
MyType_Uint,
MyType_Udint,
MyType_Ulint
};
int encode(uint8b *dest, MyType srcType, const void *src)
{
uint32b value = 0;
uint64b value64 = 0; // This is guaranted to be aligned for 64bit access
size_t expected_operand_size[] = { 1, 2, 4, 8 };
memcpy(&value64, src, expected_operand_size[(int)srcType]);
switch (srcType)
{
case MyType_Usint: value = *(uint8b*)&value64; break;
case MyType_Uint: value = *(uint16b*)&value64; break;
case MyType_Udint: value = *(uint32b*)&value64; break;
case MyType_Ulint: break;
}
// now encode value to dest
}
By the way, you should use template code here for convenience and to avoid the (useless) copy and be easier to understand (no need to have an type enum) :
template <typename T>
struct Encode
{
static uint64b convert(const void * src) { T t; memcpy(&t, src, sizeof(t)); return t; }
static uint64b convert(const T * src) { return *src; }
};
// Specialisation when we can avoid the copy
template <>
struct Encode<uint8b>
{
static uint64b convert(const void * src) { return (uint8b)*src; }
static uint64b convert(const uint8b * src) { return *src; }
};
template <typename T>
int encode(uint8b * dest, const void* src)
{
uint64b value64 = Encode<T>::convert(src);
// your encoding code here
}
// Use like this:
void * s = ...;
uint16b * s2 = ...;
uint32b * s3 = ...;
encode<uint8b>(dest, s); // No copy, calls Encode<uint32b>::do(const void*)
encode<uint16b>(dest, s2); // No copy, calls Encode<uint16b>::do(const uint16b*)
encode<uint32b>(dest, s3); // No copy, calls Encode<uint32b>::do(const uint32b*)
// or this is also safe
encode<uint16b>(dest, s); // Copy here for fixing alignment
I'm having a problem with this function. The function is supposed to return a type of StoredData.
This is my struct:
struct StoredData
{
void* data;
int size;
int type;
int compareValue;
StoredData():
size(0),
type(0),
compareValue(0){}
};
And this is my function:
StoredData SDI::Array::create(int size, int type, int compareValue)
{
StoredData temp;
void* data;
int input;
int input2;
std::cout<<"What type of data would you like to insert?"<<std::endl;
std::cout<<"1 - Integer"<<std::endl;
std::cout<<"2 - Boolean"<<std::endl;
std::cin>>input;
std::cout<<"What is the value?"<<std::endl;
std::cin>>input2;
switch (input)
{
case 1:
size = sizeof(int);
type = 0;
data = new int;
*data = (int)input2;
break;
case 2:
size = sizeof(bool);
type = 1;
data = new bool;
*data = (bool)input2;
break;
}
temp.compareValue=input2;
temp.data = data;
temp.type = type;
temp.size = size;
}
Unfortunately, I'm having a problem with the line within the case statements with
*data = (bool)input2;
The error that I'm getting is that it must be a pointer to a complete object type. I need the void variable to recognize the data, and I'm getting no luck. Anybody know a workaround?
I'm getting 2 error messages for each. The first is,
illegal indirection
And the second ones are,
error C2440: '=' : cannot convert from 'int' to 'void *'
error C2440: '=' : cannot convert from 'bool' to 'void *'
You can't dereference a void pointer. You will have to cast it to a pointer type you can dereference:
*(bool *) data = (bool) input2;
You are attempting to dereference a void pointer and set its value:
*data = (bool)input2;
This is meaningless to the compiler. What type will the result of *data be?
You need to cast the void* to something meaningful first:
*(bool*)data = (bool)input2;
Alternatively, you could initialize your dynamic variables with the correct values when you create them:
data = new int(input2);
...
data = new bool(input2);
Which wouldn't require you to cast anything.
void is an incomplete type.You may not create objects of incomplete type.
You can't dereference a plain void*, as it could point to basically anything. You aither have to make it point to something other (assigning to another pointer):
bool* bool_pointer = new bool;
*bool_pointer = static_cast<bool>(input2);
data = bool_pointer;
Or use typecasting:
*reinterpret_cast<bool*>(data) = static_cast<bool>(input2);
I have a clone_ptr implementation, as was shown in this question and I have a problem where I need to create a clone_ptr from a raw pointer returned from a function.
Here is the code:
DOMDocument* doc = impl->createDocument(
0, // root element namespace URI.
XML::X(docname.c_str()), // root element name
0); // document type object (DTD).
document.get() = *doc; //No way to assign clone_ptr document to raw doc pointer
Where document& impl are declared as follows:
clone_ptr<DOMImplementation, default_clone<DOMImplementation> > impl;
clone_ptr<DOMDocument, default_clone<DOMDocument> > document;
The createDocument function above returns a raw DOMDocument pointer and is assigned to the local variable doc, now I want to get my document clone_ptr and actually pass it the raw pointer gotten from the create document function. It seems however the compiler is not too happy with this as it says the following:
error C2440: '=' : cannot convert from 'xercesc_3_1::DOMDocument' to 'clone_ptr<T,Cloner>::pointer'
with
[
T=xercesc_3_1::DOMDocument,
Cloner=default_clone<xercesc_3_1::DOMDocument>
]
So my question is how can I allow a raw pointer to be explicitly or implicitly converted to a clone_ptr?
EDIT:
Clone specialization:
template<typename T>
struct default_clone
{
static T* clone(T* pPtr)
{
return pPtr ? pPtr->clone() : 0;
}
};
template<>
struct default_clone<DOMDocument>
{
static DOMDocument* clone(DOMDocument* pPtr)
{
DOMImplementation* impl = DOMImplementationRegistry::getDOMImplementation(XML::X("Core"));
return pPtr ? impl->createDocument(0, XML::X(""), 0) : 0;
}
};
template<>
struct default_clone<DOMImplementation>
{
static DOMImplementation* clone(DOMImplementation* pPtr)
{
return pPtr ? DOMImplementationRegistry::getDOMImplementation(XML::X("Core")) : 0;
}
};
Giving your clone_ptr implementation, and the fact that doc is a pointer, wouldn't it be document.reset(doc)?
I don't know the library but would be very surprised if document.get() returned an l-value (thus your assigning something to it seems rather strange). That doesn't mean it won't compile as very few people implement return types as const (ie. returning a constant as the temporary), just that the assign won't have the desired effect.