How to make std :: map data type for pointer function
I have a data structure like this
enum DataType_t{INT,FLOAT};
struct Data
{
DataType_T type;
void* min;
void* max;
};
Variables min
and max
depend on the value type
. I want to know if there is a way to create 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 with function pointers with different return 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 am reading this post which looks very similar but did not understand the suggested ideas, maybe you could give an example.
EDIT
I'll add a little more explanation to what I'm trying. struct Data
has a field DataType_t type
. Depending on the value, type
I need to enter the fields correctly min
and max
so that their own views are 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 Data
variable handling function 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));
}
}
since you noticed that the code is exactly the same for getMinOrMaxForXXXX
, except for return and cast types. I was thinking about 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 get the map to get a pointer to a specific specialization like
myMap[DataType_t::INT] = //PointerTo getMinOrMax<int>
myMap[DataType_t::FLOAT] = //PointerTo getMinOrMax<float>
this could help me simplify the function code in 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));
}
source to share
Use std::tuple< std::pair< DataType, std::function< void() > > > myMap;
Then we implement get by type using example with factory using this method: https://github.com/alekstheod/tnnlib/blob/master/src/Utilities/Utilities/Design/Factory.h
for getting by type look here: https://github.com/alekstheod/tnnlib/blob/master/src/Utilities/Utilities/MPL/Tuple.h
From now on:
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 where I take the first element from a tuple and execute a function. Now instead of getting the first element, we need to find the element using the 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 the call to it will look like this:
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 the return type of a function or the function itself. Or, you can keep it as is and bind if additional arguments are required.
source to share
Instead, I would use a template class:
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 structure as
enum DataType_t{INT,FLOAT};
struct Data
{
DataType_T type;
void* min;
void* max;
};
then you have to do 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);
}
}
source to share