4

I have a data structure like this

enum DataType_t{INT,FLOAT};
struct Data
{
   DataType_T type;
   void* min;
   void* max;
};

The variables min and max depend on the value of type. I want to know if there is a way to create a std::map like

std::map<DataType_t, SomeFcnPtr> myMap;
myMap[INT] = ?? // Here should be a pointer to a function like int(*FcnPtr)(Data d, bool min);
myMap[FLOAT] = ?? // Here should be a pointer to a function like float(*FcnPtr)(Data d, bool min);

Is there a way to create such a map that have function pointers with different return data types?

In the end I want to use it to normalize values

float normalizedValue = (readValue - mayMap[INT](intData, true)) / (mayMap[INT](intData, false) - mayMap[INT](intData, true))

I read this post that looks really similar but didn't understand the proposed ideas, maybe you could give an example.

EDIT

I will add a little bit more explanation on what I am attempting. struct Data has a field DataType_t type. Depending on the value of type I need to properly cast the min and max fields to have their proper representations as int or float. One possible way could be

int getMinOrMaxForINT(Data aData, bool min)
{
   if(min) return *((int*)aData.min));
   return *((int*)aData.max));
}

and similarly

float getMinOrMaxForFLOAT(Data aData, bool min)
{
   if(min) return *((float*)aData.min));
   return *((float*)aData.max));
}

finally in some function processing Data variables I could do

void someFunction(int value, Data aData)
{
   float normalizedValue = 0;
   if(aData.type == DataType_t::INT)
   {
      normalizedValue = (value - getMinOrMaxForINT(aData, true)) / (getMinOrMaxForINT(aData, false) - getMinOrMaxForINT(aData, true));
   }
   else if(aData.type == DataType_t::FLOAT)
   {
      normalizedValue = (value - getMinOrMaxForFLOAT(aData, true)) / (getMinOrMaxForFLOAT(aData, false) - getMinOrMaxForFLOAT(aData, true));
   }
}

as you notice the code is exactly the same for the getMinOrMaxForXXXX except for the return and cast types. I thought of using a template like

template <typename T>
T getMinOrMax(Data aData, bool min)
{
       if(min) return *((T*)aData.min));
       return *((T*)aData.max));
}

but the problem is how to have the map to get a pointer to a certain specialization, for example

myMap[DataType_t::INT] = //PointerTo getMinOrMax<int>
myMap[DataType_t::FLOAT] = //PointerTo getMinOrMax<float>

this could help me simplifying the function code in the process function

void someFunction(int value, Data aData)
{
   float normalizedValue = (value - myMap[aData.type](aData, true)) / (myMap[aData.type](aData, false) - myMap[aData.type](aData, true));
}
13
  • @Jarod42 could you give me an example? I don't see how boost::variant can help. Commented May 22, 2015 at 20:33
  • Your sample code is not consistent. If myMap values are function pointers, why have they getMin, getMax methods? Commented May 22, 2015 at 20:34
  • Sorry @simon, edited my question to reflect myMap values are pointers to functions. I think it is consistent now, any idea on how to construct such a map? Commented May 22, 2015 at 20:38
  • well, since INT is of DataType_t I assume it could be used as myMap[SomeVariable] where someVariable is of DataType_t. Commented May 22, 2015 at 20:43
  • @BRabbit27: but do you now the return type in that line compile time? Commented May 22, 2015 at 20:44

2 Answers 2

1

Use std::tuple< std::pair< DataType, std::function< void() > > > myMap;

Then implement get by type see example with a factory using this technique here: https://github.com/alekstheod/tnnlib/blob/master/src/Utilities/Utilities/Design/Factory.h

for get by type look here: https://github.com/alekstheod/tnnlib/blob/master/src/Utilities/Utilities/MPL/Tuple.h

Starting from here:

    int retInt(){
  return 1;
}

float retFloat(){
  return 1.f;
}

char retChar(){
  return 'a';
}

int main(int, char *[])
{

  std::tuple< std::pair< int, std::function< int () > >,
             std::pair< float, std::function< float() > >,
             std::pair< char, std::function< char () > > > map;

  std::get<0>(map).second = retInt;
  int result = std::get<0>(map).second();
  return 0;
}

This is a simple example when I take a first element from a tuple and execute the function. Now instead of getting the first element we need to find an element by using a type. A bit of metaprogramming:

namespace detail{

template <class T, std::size_t N, class... Args>
struct get
{
  static const auto value = N;
};

template <class T, std::size_t N, class... Args>
struct get<T, N, T, Args...>
{
  static const auto value = N;
};

template <class T, std::size_t N, class U, class... Args>
struct get<T, N, U, Args...>
{
  static const auto value = get<T, N + 1, Args...>::value;
};

}

template <class T, class... Args>
std::pair< T, std::function< T() > >& get(std::tuple<Args...>& t)
{
    using Entry = std::pair<T, std::function<T()> >;
    return std::get<detail::get<Entry, 0, Args...>::value>(t);
}

Then call to it will look like:

int main(int, char *[])
{

  std::tuple< std::pair< int, std::function< int () > >,
             std::pair< float, std::function< float() > >,
             std::pair< char, std::function< char () > > > map;

  get< int >(map).second = retInt;
  int result = get<int>(map).second();

  get< float >(map).second = retFloat;
  float result2 = get< float >(map).second();

  return 0;
}

You can replace a return type of the function or the function itself. Or you can keep it as it is and do a bind in case some more arguments are needed.

Sign up to request clarification or add additional context in comments.

2 Comments

I wansn't able to catch the idea you proposed, I'm not very used to templates. Could you give me an example?
I added an example for you. Not really based on your code but you can adapt it very quickly.
0

I would use a template class instead:

template <typename T>
struct Data
{
   T* min; // Do you really need pointer here ?
   T* max;
};

So your functions become:

// I would prefer split the function getMin(const Data<T>& aData)
// and getMax(const Data<T>& aData) or simply directly use (*aData.min)
template <typename T>
T getMinOrMax(const Data<T>& aData, bool min)
{
    return min ? *aData.min : *aData.max;
}

And

template <typename T>
float ComputeNormalizedValue(T value, const Data<T>& aData)
{
    return float(value - *aData.min) / (*aData.max - *aData.min);
}

If you really need to keep your struct as

enum DataType_t{INT,FLOAT};
struct Data
{
   DataType_T type;
   void* min;
   void* max;
};

then you have to make your check at runtime in your template methods:

template <typename T> bool isCorrectType(const Data& aData);
template <> bool isCorrectType<int>(const Data& aData) { return aData.type = INT;}
template <> bool isCorrectType<float>(const Data& aData) { return aData.type = FLOAT;}

template <typename T>
T getMin(Data aData, bool min)
{
    if (!isCorrectType<T>(aData)) { throw std::runtime_error("Bad type"); }
    return *((T*)aData.min));
}

template <typename T>
T getMax(Data aData, bool min)
{
    if (!isCorrectType<T>(aData)) { throw std::runtime_error("Bad type"); }
    return *((T*)aData.max));
}

template <typename T>
float ComputeNormalizedValue_impl(int value, const Data& aData)
{
    return float(value - getMin<T>(aData)) / (getMax<T>(aData) - getMin<T>(aData));

}

float ComputeNormalizedValue(int value, const Data& aData)
{
    switch (aData.type)
    {
        case INT: return ComputeNormalizedValue_impl<int>(value, aData);
        case FLOAT: return ComputeNormalizedValue_impl<float>(value, aData);
    }
}

2 Comments

If I use the template approach as proposed, how can I create an std::vector of Data that would allow me to use different types of Data? Say, std::vector<Data<?>> data; data.push_back(/*Data<int>*/); data.push_back(/*Data<float>*/);?
@BRabbit27: You cannot as this, you may create a base class, but then you have again to cast to the correct type if you don't provide virtual methods. (and store (smart) pointers).

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.