Template function pointer argument

I have a container of variables that are created from template class and types are detected in runtime (stored in type as string).
But.. Let's for example say that I want to interpolate the variables, and those variables are of different datatypes as said above.
I want to run more than one function to not use the same compare-code all over again:

1
2
3
4
5
6
7
8
9
10
11
12
void ClassA::CompareDTAndDoSomething(TimeTag time, long value, long valueTo)
{
	if (type == "char *")		AddInterpol<CHAR>(time, (CHAR)value, (CHAR)valueTo); else
	if (type == "unsigned char *")	AddInterpol<UCHAR>(time, (UCHAR)value, (UCHAR)valueTo); else
	if (type == "short *")		AddInterpol<SHORT>(time, (SHORT)value, (SHORT)valueTo); else
	if (type == "unsigned short *")	AddInterpol<USHORT>(time, (USHORT)value, (USHORT)valueTo); else
	if (type == "int *")		AddInterpol<INT>(time, (INT)value, (INT)valueTo); else
	if (type == "unsigned int *")	AddInterpol<UINT>(time, (UINT)value, (UINT)valueTo); else
	if (type == "long *")		AddInterpol<LONG>(time, (LONG)value, (LONG)valueTo); else
	if (type == "unsigned long *")	AddInterpol<ULONG>(time, (ULONG)value, (ULONG)valueTo); else
	if (type == "float *")		AddInterpol<FLOAT>(time, (FLOAT)value, (FLOAT)valueTo);
}


I have this function: CompareDTAndDoSomething(...);
Sometimes I want to pass more arguments and more than one function.
so I want to use a function pointer with template instead, but thats when I run intro problems.

for example I want if possible to do something like this:
1
2
if (type == "char *") funcPtr<CHAR>(time, (CHAR)value, (CHAR)valueTo); else
if (type == "unsigned char *") funcPtr<UCHAR>(time, (UCHAR)value, (UCHAR)valueTo); else

....etc..

then the ClassA::Compare-member function need some other argument too, or variable argument list:

1
2
3
4
5
void ClassA::CompareDTAndDoSomething(FuncPtr<T> function, ...)
{
if (type == "char *") funcPtr<CHAR>(time, (CHAR)value, (CHAR)valueTo); else
....etc..
}


so instead of having void ClassA::CompareDTAndSomething2(newFunction)
and all the if sentences again and another function, i just want to reuse the same member-function as pass function pointers to it:
1
2
3
CompareDTAndDoSomething(AddInterpol, arg1, arg2, arg3, ..etc);
CompareDTAndDoSomething(SetInterpol, arg1, arg2);
CompareDTAndDoSomething(SomethingElseFunc, arg1);

AddInterpol and SetInterpol should not have all the compares of types, thats what CompareDTAndDoSomething function should do.

AddInterpol and SetInterpol are template functions however. but its important that CompareDTAndDoSomething is not a template class.
Any ideas how to pass template-functions into arguments of a member-function like this?
To prevent multiple lines of if-compares of the datatypes above?
Hope this wasnt to messy to understand what Im asking about.
Thanks
Last edited on
Are you aware of variadic templates? Overload resolution rules?

Your chosen design is highly error-prone and sacrifices type safety and imposes serious, subtle restrictions on the real types of arguments for no apparent reason.

Can I prevent multiple lines of if-compares of the datatypes above
If you insist on avoiding the type system by encoding type in a variable whose value is not a constant expression,
there's no way to avoid dispatching at runtime on that value. That means an if-chain or an equivalent operation (e.g., data-structure lookup, switch-case, etc.)

but its important that CompareDTAndDoSomething is not a template class.
CompareDTAndDoSomething isn't a class. Is making it a template member function acceptable?

Any ideas how to pass template-functions into arguments of a member-function like this?

It appears you're looking for the proposed std::invoke.
http://en.cppreference.com/w/cpp/utility/functional/invoke

In the likely case that preemptive C++17 support isn't available to you; Boost.Fusion provides an alternative.
http://www.boost.org/doc/libs/1_63_0/libs/fusion/doc/html/fusion/functional/invocation/functions/invoke.html

Perhaps you may benefit from a type-safe generic union type, like the proposed std::variant<>
http://en.cppreference.com/w/cpp/utility/variant
There's a Boost alternative:
http://www.boost.org/doc/libs/1_63_0/doc/html/variant.html

There is almost certainly a better way to approach your problem. If you explain your goal, you are likely to get better help.
http://xyproblem.info/
Last edited on
I have not seen the variadic template no, that could be a goal to pass the all the datatype-names via a variadic template however, but if that will solve any of the problems i dont know.

Im sorry to say that my code is pretty huge, so i cant post all of it here, i tried to do my best to describe it in the introduction-post, and most of the things in that codebase with the templates do work fine. I use abstract class some places, derived classes, base classes with pure virtual functions, and i check datatypes at runtime with decltype(VAR) and so on, i have collectors of base objects in vectors which are allocated with new of other classes. all the types are stored in the objects and their respective names are stored in strings (like what the "type" string in the introduction post represents).

but its important that CompareDTAndDoSomething is not a template class.
CompareDTAndDoSomething isn't a class. Is making it a template member function acceptable?


No, sorry for being unclear about this. I meant that its important that CompareDTAndDoSomething is not a template member function. But, note, it can take a template functon-pointer argument if that is possible?

Like that funcPtr<CHAR> or funcPtr<UCHAR> and so on, etc. should be called if any of the branches is taken.

Boost is not an option.
Last edited on
I check datatypes at runtime with decltype
decltype is a compile-time operation which results in the type of an expression. Type cannot be queried at runtime except via dynamic_cast and typeid.

At any rate, it would be better to store type information in immutable and comparable typeinfo objects rather than strings, even if the use of RTTI is quite often a symptom of bad design.

But, note, it can take a template functon-pointer argument

No, it's not possible. Member functions with different signatures have different types. Did you consider type-erasure based techniques (e.g., std::function<>)? What is the relationship between each of the possible types that can be passed to CompareDTAndDoSomething? Can you create an object to represent a more generic instance of those members (and maybe apply the visitor pattern to it?)

Boost is not an option.

That's unfortunate. It should be.

It's important that CompareDTAndDoSomething is not a template member function

Why?
Is it acceptable to make it a template class? A generic closure? What's your concern about templates?
Your misunderstanding about the type system indicates you may be making a serious design error by abandoning the type system when it's not necessary to do so.
Last edited on
No, I think you misunderstood, CompareDT.. is not inside a template class. The class has a couple of template member functions that pass different datatypes to add different variables in a vector-container - like AddInterpol<T> and so on. CompareDT.. is called in the Graphical User Interface when somebody (like me) write in values (it may be floats, ints,etc. and even pointers to other objects (that sometimes are passed to comboboxes). I have to handle the different types like this, because of user-input.

You're right about dynamic_cast, i forgot to mention that. I have that in another function:
1
2
3
4
5
6
7
template <class T>void Variable<T>::get(BaseValue *v)
{
	dynamic_cast<Value<T> *>(v)->valueSync = valueSync;
	dynamic_cast<Value<T> *>(v)->value = value;
	dynamic_cast<Value<T> *>(v)->valueTo = valueTo;
	dynamic_cast<Value<T> *>(v)->genPtr = genPtr;
}

I use this to get the variables of different types, that are handled in the program with and without user input.

Its really complicated. Yeah, Im not a very experienced guy with templates, and probably dont see or understand everything right away. I know the basics of templates. But, I just want to keep it simple, i dont want my code to be too complicated to maintain.
But all the tips are appreciated, I would have to look into some of your tips. like for example std::function<> i dont think i have ever used that before. If i need it tho.

Edit: in that function: valueSync is a pointer to the address where the variable is. value and ValueTo are the actual contents (int, char, float or anything else) and genPtr is pointer to a generator, that can be anything.
Last edited on
I think you misunderstood, CompareDT.. is not inside a template class.

Maybe, but I don't think so.
By "Is it acceptable to make it a template class?" I meant "is it acceptable to make CompareDT. itself a template class?" I was motivated to ask because you didn't provide a rationale regarding why it cannot be a template.

The class has a couple of template member functions that pass different datatypes to add different variables in a vector-container - like AddInterpol<T> and so on

Why does the class need to have everything the user inputs? Why should the caller not store them?

I don't want my code to be too complicated to maintain

Your code will be more robust and more general if you use the type system to help you.

Special-casing and the subtle restrictions that constrain the other facilities (e.g., void*, RTTI, c-style casts etc.,) make them insufficient for a single general solution.

Example:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
# include <vector>
# include <iostream>
# include <iomanip>

// For demonstration purposes
# include <typeinfo> 
# include <boost/spirit/include/qi.hpp>
# include <boost/spirit/include/qi_match.hpp>

// Do you really need this container?  Why can't the caller store them?
std::vector<boost::variant<unsigned*, double, std::string>> vec;

struct Display: boost::static_visitor<> {
  template<typename T> void operator()(T const &value) const {
    std::cout << "I was passed an instance of type:\n\t"
              << typeid(T).name() << "\nwith a value of:\n\t"
              << value << "\n";
  }
  // gets called for unsigned*
  void operator()(unsigned *const& value) const {
    std::cout << "I was passed a pointer to unsigned int, with a value of \n\t"
              << value << "\n";
  }
  // gets called for std::string: note:  nontrivially constructible
  // variant is generic 
  void operator()(std::string const& value) const { 
    std::cout << "I was given a std::string containing the token \n\t"
              << value << "\n";
  }
};

template <typename From, typename To = From>
static void push_as(From const& val) { vec.emplace_back((To)(val)); }

int main() {
  // The parsing logic isn't intended to be the interesting bit.
  //
  // according to the grammar specified below, it emplaces variant
  // objects of the appropriate type inside the file-local vector
  // of variants named vec (line 11)
  using namespace boost::spirit::qi; // probably shouldn't do this
  auto pointer_value = (string("uint*") >> int_parser<std::uintptr_t, 16>{}
    [push_as<std::uintptr_t, unsigned*>]);
  auto double_value = (string("double") >> double_[push_as<double>]);
  std::cin >> std::noskipws >> phrase_match
    ((pointer_value | double_value |
      (as_string[+(char_ - eol)])[push_as<std::string>]) % eol, blank);

  // Visit each elt. in the collection:
  // It should be relatively easy to apply the visitor pattern to a class 
  // that represents only one of the applicable types
  for (auto v: vec) boost::apply_visitor(Display{}, v);
}


See a live demo, here:
http://coliru.stacked-crooked.com/a/ef235115c9dc01e4
Last edited on
Ok. I will try to redesign the whole project. Too bad, because I almost got it right :S
I have to learn all these new features in C++11, and more about templates, I do only have basic skills. If I understand it better im sure I will be able to use it better too.

I will not use boost as I said earlier. I won't go into details about why.

"is it acceptable to make CompareDT. itself a template class?"

No, it is not. Unless I can have vector containers of templated (different datatypes) objects. I need to change the whole design. I which I am looking into now, but I just have to learn a few more things. I could even do all this without templates, but I think the codebase would be alot larger. But I hoped templates would make the code smaller and more compact and easy to read. But there is pros and cons with this. The type system is of course the biggest problem in my project. I started this large project before C++11 came, and seems like auto has changed, and also some new features that might be relevant.
I know that you can't use Boost. You can do the parsing on your own; you could replace the use of variant with your own class that represents exactly one of the applicable types -- i.e., a discriminated union. There's plenty of info floating around regarding how to write those: it doesn't need to be fully generic if you will only use it in this one case.

Unless I can have vector containers of templated (different datatypes) objects.

There are no "heterogeneous" containers in C++. But there's usually some way to get around this.
Consider plain-old inheritance --store owning pointers to base classes.
If that's not sufficient, you can use a discriminated union type like variant.
If that's not sufficient, you can use a form of interface-preserving type erasure (you will need to roll your own). Type-erasure comes with a bit of a run-time cost vs. templates:
http://en.cppreference.com/w/cpp/utility/any
http://www.boost.org/doc/libs/1_63_0/doc/html/any.html
Finally, if none of these are sufficient for whatever reason, you can resort to bending the type system with ugly casts.

I'll try to write an similar example later that eliminates the use of variant for first-party functionality.

I started this large project before C++11 came, and seems like auto has changed, and also some new features that might be relevant.

Most likely - it was a major change.
BjarneStroustrup wrote:
... the range of user-defined types that can be cleanly and safely expressed has grown with the addition of features such as initializer-lists, uniform initialization, template aliases, rvalue references, defaulted and deleted functions, and variadic templates. Their implementation eased with features, such as auto, inherited constructors, and decltype. These enhancements are sufficient to make C++11 feel like a new language.

http://www.stroustrup.com/C++11FAQ.html#what-features
Last edited on
Consider plain-old inheritance --store owning pointers to base classes.

Thats what I do man, that's what I do.

By that I mean it works pretty well. Except for the templated code i posted in the introduction with all the compares.

Well, I will try to keep most of the code, but if I can't find a way to make it more compact, then I have to redesign some of it. Well, now I have alot to learn and read about, and then see if I can do any changes to and fix some of the code that I think is ugly.
Last edited on
Topic archived. No new replies allowed.