Search code examples
c++templatesgenericsgeneric-programming

C++: create Pointertype from runtime dimension information


I want to do something like this:

template<typename CType, unsigned int targetDimensions> struct GeneratePointerType
{
    typedef typename GeneratePointerType<CType, targetDimensions-1>::type* type;
    static const unsigned int dimensions = GeneratePointerType<CType, targetDimensions-1>::dimensions+1;
};
template<typename CType> struct GeneratePointerType<CType, 0>
{
    typedef CType type;
    static const unsigned int dimensions = 0;
};

Dictionary<int, GeneratePointerType<int, valueDimensions>::type >

So basically I want to instantiate a container template for a pointer type, but I don't know the pointer level of the pointer to create before runtime, so the approach from above of course can't wocompile, as "dimensions" isn't a compile-time constant.

Is there a way (that is compatible to pure C++03 without any C++11 only features) to achieve the intended behavior?

EDIT: "If you could tell us the real problem, we might have a better solution." "So to clarify, you want a map of N dimensional array/objects, where N is a runtime value? Wierd. I can't imagine a use-case" OK, let me tell you a few words about the usecase:

I have a class Template Dictionary, which is basically just behaving like the C# or Java Dictionary generics.

This class is used in C++ wrapper library around a C library.

Therefor I have conversion functions, that convert data between instances of C++ container classes and instances of C container structs.

As you know, C doesn't have generics, so while in C++ I can create containers like this:

Dictionary<int, int**> dict;

in C I have to do it like this:

CDictionary dic;
dic.keytype = TYPECODE_INT;
dic.valuetype = TYPECODE_INT;
dic.valueDimensions = 2;

Now when converting a C Dictionary into a C++ Dictionary, I struggle on how to generate the correct number of *'s, as the info stored inside the C dictionary isn't a compile time constant.

EDIT2: Actually the point, that I am getting that runtime-dimension-counts from the underlying C interface doesn't matter, as the C lib creates those structs from serialized data, that comes in over the network and as its impossible to know at compiletime of the lib, how many array-dimensions an array, that comes in over the network from another app in potentially another programming langauge, will have, a C++ implementation of the deserialization would still have to determine valuetypes for Dictionaries from runtime information about the array dimensions.

EDIT3: "OK, I think you need to illustrate how you want to use this structure, because I clearly didn't interpret your question correctly, and none of this information is in the question. Can you edit in some pseudocode showing what you're trying to achieve?" call to public interface:

Dictionary<int, int*> dic;
int* arr = new int[10];
dic.put(1, arr, a0);
delete arr;

send(dic); // dic gets serialized and sent over the netwowork

When receiving a serialized dic, I want to do deserialize it back, before passing it to a callback:

// read out typecodes and dimnesions from the serialized data
// [...]
// create the Dictionary from that info
switch(valueTypeCode)
{
    case TYPECODE_SHORT:
        return new Dictionary<int, GeneratePointerType<short, valueDimensions>::type>[size]();
    case TYPECODE_INTEGER:
        return new Dictionary<int, GeneratePointerType<int, valueDimensions>::type>[size]();
}
// fill it with the deserialized payload afterwards
// [...]

Solution

  • If the parameter N isn't known at compile time, you can't make it part of your static type. So, you definitely have to use some kind of runtime dispatch.

    Likely options are:

    1. just wrap the C structures shallowly (providing operator overloads and other methods, encapsulating the underlying data) and access it exactly the same way

      class Dictionary {
          CDictionary *inner;
      public:
          // C++ syntactic sugar here
      };
      
    2. if N is bounded above by some reasonable value (eg, you can assume N < 10), you could instantiate a template class for every valid N, which implements an abstract interface. Then your template instantiations are hidden in a translation unit which only exposes a public (virtual) interface and a factory function; actual access uses runtime polymorphism

      class AbstractDictionary {
      public:
          virtual ~AbstractDictionary() = 0;
          // virtual methods
      };
      AbstractDictionary* wrap(CDictionary *);