views:

562

answers:

5

Is there anyway to do something like this?

(correct pointer datatype) returnPointer(void* ptr, int depth)
{

    if(depth == 8)
     return (uint8*)ptr;
    else if (depth == 16)
     return (uint16*)ptr;
    else
     return (uint32*)ptr;
}

Thanks

+4  A: 

No. The return type of a C++ function can only vary based on explicit template parameters or the types of its arguments. It cannot vary based on the value of its arguments.

However, you can use various techniques to create a type that is the union of several other types. Unfortunately this won't necessarily help you here, as one such technique is void * itself, and getting back to the original type will be a pain.

However, by turning the problem inside out you may get what you want. I imagine you'd want to use the code you posted as something like, for example:

void bitmap_operation(void *data, int depth, int width, int height) {
  some_magical_type p_pixels = returnPointer(data, depth);
  for (int x = 0; x < width; x++)
    for (int y = 0; y < width; y++)
      p_pixels[y*width+x] = some_operation(p_pixels[y*width+x]);
}

Because C++ needs to know the type of p_pixels at compile time, this won't work as-is. But what we can do is make bitmap_operation itself be a template, then wrap it with a switch based on the depth:

template<typename PixelType>
void bitmap_operation_impl(void *data, int width, int height) {
  PixelType *p_pixels = (PixelType *)data;
  for (int x = 0; x < width; x++)
    for (int y = 0; y < width; y++)
      p_pixels[y*width+x] = some_operation(p_pixels[y*width+x]);
}

void bitmap_operation(void *data, int depth, int width, int height) {
  if (depth == 8)
    bitmap_operation_impl<uint8_t>(data, width, height);
  else if (depth == 16)
    bitmap_operation_impl<uint16_t>(data, width, height);
  else if (depth == 32)
    bitmap_operation_impl<uint32_t>(data, width, height);
  else assert(!"Impossible depth!");
}

Now the compiler will automatically generate three implementations for bitmap_operation_impl for you.

bdonlan
In C#, you can return an object type, and cast it to your desired object (provided you know the type). Or you can use generics. Can you do something similar in c++?
Robert Harvey
The closest C++ has to an object type is void *. There's also boost's variant type.
bdonlan
I guess i would use boost variant too.
Johannes Schaub - litb
+7  A: 

If you can use a template argument instead of a normal parameter, you can create a templated function that returns the correct type for each depth value. First there needs to be some definition of the correct type according to depth. You can define a template with specializations for the different bit sizes:

// template declaration
template<int depth>
struct uint_tmpl;

// specializations for certain types
template<> struct uint_tmpl<8>  { typedef uint8_t type; };
template<> struct uint_tmpl<16> { typedef uint16_t type; };
template<> struct uint_tmpl<32> { typedef uint32_t type; };

The this definition can be used to declare a templated function that returns the correct type for every bit value:

// generic declaration
template<int depth>
typename uint_tmpl<depth>::type* returnPointer(void* ptr);

// specializations for different depths
template<> uint8_t*  returnPointer<8>(void* ptr)  { return (uint8_t*)ptr;  }
template<> uint16_t* returnPointer<16>(void* ptr) { return (uint16_t*)ptr; }
template<> uint32_t* returnPointer<32>(void* ptr) { return (uint32_t*)ptr; }
sth
This offers no advantage over a solution without template like returnPointer8(), returnPointer16() and returnPointer32(), because the template argument has to be constant (i.e known by the programmer) anyway.
Jem
That the template arguments have to be known at compile time is a general property of templates. Still this is more flexible than differently named functions, for example you could define something like template<int depth> ptradd(void* ptr, int val) { *returnPointer<depth>(ptr) += val; }. This works for all depths. With differently named returnPointer functions you would need to also declare several separate variants of ptradd. For the templated version one ptradd function is enough.
sth
+1  A: 

You can allocate some memory on the heap, and return a void* that you cast to the type that was allocated. Its a dangerous and unsafe way of working and is an old C trick.

You could return a union that contains all valid datatypes (and a selection indicator).

You could use templates, which is the recommended C++ way for this kind of thing.

You could provide a set of overloaded functions that take a parameter (of each type) as a reference - the compiler will decide which function to call based on the datatype. I often prefer this way as I think its the simplest.

gbjbaanb
A: 

No; you can't do that in C++. The correct answer is to return void *.

Think about it from the opposite side of the call -- and from the compiler point of view at that:

How would the compiler be able to verify if the return value is used correctly (like assigned to a variable of the proper type, for example), if it cannot possibly know which of the three return types will be returned?

At that point, the notion of assigning "one of multiple types" to the return value becomes meaningless. The return type of a function has no other purpose in life than to make it possible for the compiler to do it's job; the compiler needs "one" type to be able to do type checking. Since you don't know which one it is until runtime, the compiler can't do the type checking for you. You have to tell the compiler to "stop trying" to match the return value to any specific pointer type -- hence, return a void *.

If your depth arguments is known at compile time, you can alternatively use a set of templates like @sth demonstrated, or use a set of separate independent functions, or use a set of related functions that call a shared implementation function and then cast the return to the proper type. Which one you chose is a mostly an aesthetic decision.

If the value of depth is not know until run-time, then you should probably return void *.

Now, I'm assuming that your actual implementation actually does something to produce the pointer other than what your sample code shows. Your sample code is not an actual function; it's more like trying to duplicate what a cast does. A cast is not a function call; it's a compiler directive to try to "make" it's operand into a specific type (exactly 'how', is a long story for another post). It's not a C++ language operation, but a compiler operation. You can't rewrite that in C++ itself.

Euro Micelli
A: 

can you do this:

if(depth==8) (uint8*)returnPointer(void* ptr, int depth){ // etc