Can I use template parameter as argument to non-template method? - c++

I am looking to move some of the code of within a template method to a non-template method in order to decrease the binary size.
There is a template class called 'Target', as illustrated below
template<TargetType K, typename V = plat_target_handle_t>
class Target
{
.............
..............
};
TargetType is an enum data type.
template<>
template< TargetType T>
std::vector<Target<T> >
Target<TARGET_TYPE_X>::getChildren(const TargetState i_state) const
{
std::vector<Target<T> > l_children;
for ( int i=0; i < elements_in_some_list ; ++i)
{
/*If the current entry in some_list match my critera, add to the l_children */
}
}
TargetType is an enum data type and TARGET_TYPE_X is one of the enum values.
I want to move all the logic to select the children to a global method, lets say getChildrenHelper.
getChildrenHelper is declared as below.
void getGhildrenHelper(const TargetType i_targetType,
const TargetState i_targetstate,
std::vector<Target<TARGET_TYPE_ALL>> & io_children);
And then the getChildren method would eventually look like
template<>
template< TargetType T>
std::vector<Target<T> >
Target<TARGET_TYPE_X>::getChildren(const TargetState i_state) const
{
std::vector<Target<T> > l_children;
childHelper(T,i_state,l_children);
return l_children;
}
My guess is this cannot be done, though the native compiler that I am working with did not through an error.
However there is another existing code where the similar concept is working perfectly fine
template< TargetType K >
inline ReturnCode putParam(const Target<K>& i_target,
const RingID i_ringID,
const RingMode i_ringMode)
{
ReturnCode l_rc = FAPI2_RC_SUCCESS;
// Find the string in the SEEPROM
l_rc = findInImageAndApply(i_target, i_ringID, i_ringMode);
return l_rc;
}
fapi2::ReturnCode findImageAndApply(
const fapi2::Target<fapi2::TARGET_TYPE_ALL>& i_target,
const RingID i_ringID,
const fapi2::RingMode i_ringMode)
{
................
................
}

It is quite common for template functions to invoke ordinary, non-template functions in order to execute a large chunk of code that does not need or use any template parameters. This is a common technique for avoiding template-generated code bloat.
In your case, TargetType appears is a template parameter, and there is no such class. As such:
void getGhildrenHelper(const TargetType i_targetType,
const TargetState i_targetstate,
std::vector<Target<TARGET_TYPE_ALL>> & io_children);
that by itself should not compile, since TargetType appears to be a template parameter, and not a class name, based on the code in your template specialization.
But, your code might be ambiguous, here. In any case, if neither TargetType and TargetState, nor Target<TARGET_TYPE_ALL> are template parameters, this would make this an ordinary function, and it can certainly be invoked from a template function, with matching parameters.
A template function can do anything that an ordinary function does, including calling other functions, or using other templates. The requirements are the same as for any other function: matching function parameter types, etc...

Related

Correct usage of ellipsis and template parameters

Turns out I need to build a function that takes a variable number of arguments of no predetermined types,something like:
myFun(int param1, char param2, short param3....);
And I thought about using variadic template functions.
This is actually a member function of a class which is inside a namespace, this is my header file:
//USART.hh file
namespace usartNameSpace
{
class USART
{
public:
USART();
~USART();
template <typename firstArgument, typename ...Arguments> bool parseUserCommands(firstArgument &argument, Arguments &... args);
};
}
This is the implementation(I have omitted both the constructor and destructor)
//USART.cpp file
using namespace usartNameSpace;
template <typename firstArgument, typename ...Arguments> bool USART::parseUsercommands(firstArgument &argument, Arguments&... args)
{
//stuff the function does
}
I will say it again, my goal is to be able to create a method that takes a variable number of parameters of no particular type, with this conception I think I'm able to reach that, however, when calling the method from the main.cpp file, I get the following error at compile time:
undefined reference to `bool usartNameSpace::USART::parseUserCommands<int, char>(int&, char&)'
collect2: error: ld returned 1 exit status
make: *** [makefile:68: mainProject.elf] Error 1
I do not really know what is wrong with my conception of things here, someone told me to try to put the implementation in the same header where the definition lies but that did not work neither(If someone could explain this one also, it would be very nice).
Lastly, here is my main file:
#include "USART.hh"
usartNameSpace::USART coms(USART2);
int n = 8;
char m = 'a';
int main(void)
{
coms.parseUserCommands<int, char>(n, m);
return 0;
}
Template Definition vs Declaration TL;DR
Put the definition of template functions, classes and methods (if isolated to a single class method) in the header file. Anything else can be defined in a source file.
Longer
The problem is that is not possible to have templates definitions (classes, functions and methods) in source files as the compiler need to know the instantiated type when it compiles the file, it can't leave it as a template parameter. In this case you have to put the definition of the template method with its declaration in the header. However, this doesn't mean you have to put non-template method's definitions in the same header, you can even create methods with the same name but aren't templated and split the declaration and definition (as far as my brief testing went).
Variadic Template Use
The pattern you appear to be using is a recursive template parameter function (method in this case but works for both). The key is that this is a recursive pattern thus you need a base method that just takes one (in this case template) argument. The recursive function takes a template initial argument and a second variadic parameter pack. You do what you want with the first argument, then call the method again parsing the variadic arguments (args... with the ellipsis). This will unfold the pack until you it the base class.
Another method would be to take a tuple of variadic arguments and manipulate the tuple so you can access all of the elements in a single function call, removing the need for a recursive variadic call pattern.
You could also (I believe) use std::make_tuple on the variadic pack to make it into a std::tuple and in that case you wouldn't need recursive pattern or the firstArgument parameter, just a function that take Args... args but I didn't explore that in testing. Some homework for you.
Code
Note: This is the code that was tested (briefly). I didn't explore in depth the solution with tuples but provided resources below that will hopefully guide you.
/// USART.hh
/// \note added header guards for completeness
#ifndef USART_H ///< header guard
#define USART_H
#include <iostream>
#include <string>
namespace usartNameSpace
{
class USART
{
public:
USART();
~USART();
/// Base case for recursive call
template<typename FirstA>
bool parseUserCommands(FirstA first)
{
std::cout << first << std::endl;
return true;
}
/// Variadic Resursive call
template<typename FirstA, typename... Args>
bool parseUserCommands(FirstA first, Args... args)
{
std::cout << first << std::endl;
return parseUserCommands(args...);
}
/// Non-template method with the same name as a templated method
bool parseUserCommands();
/// Non template method
void printer(std::string str);
};
}
#endif // USART_H
/// USART.cpp
#include <USART.hh>
namespace usartNameSpace
{
/// Implement all non-template methods in source file (*.cpp)
USART::USART()
{ std::cout << "USART constructor" << std::endl; }
USART::~USART()
{ std::cout << "USART destructor" << std::endl; }
bool USART::parseUserCommands()
{
std::cout << "No arguments" << std::endl;
return true;
}
void USART::printer(std::string str)
{ std::cout << str << std::endl; }
}
/// main.main.cpp
#include <USART.hh>
#include <iostream>
auto main() -> int
{
auto n = 8;
auto c = 'a';
auto s = "Hello";
auto b = true;
usartNameSpace::USART usart;
usart.parseUserCommands(n, c); ///< Two arguments
usart.parseUserCommands(s); ///< One argument
usart.parseUserCommands(n, c, s, b); ///< Four arguments
usart.printer("Bye!"); ///< Non-template method
usart.parseUserCommands(); ///< Non-template method of same name as templated one
return 0;
}
# Output
# Build and run instructions included for completeness
# I can provide a manual build command if requested.
$ bpt build -t :c++20:gcc-11 -o build
$ ./build/main
USART constructor
8
a
Hello
8
a
Hello
1
Bye!
No arguments
USART destructor
Compilers and Tools
GCC-11.3.0
bpt build system
Links and Resources Used
Variadic member function of template class : SO
Why can templates only be implemented in the header file? : SO
Pretty-print std::tuple : SO
For exploring manipulation parameter packs , tuples and variadic fold expressions
Parameter pack : cppreference
std::tuple : cppreference
std::make_tuple : cppreference

Use Variadic templates (or a similar mechanism) to pass list of types to a function

I would like to implement a class wrapper for database.
Currently, I'm working on a createTable function.
The way I have tried to make it work is, that the user specifies the types
as a template parameters, and the column names as an initialiser list,
this is the template of the function:
template <typename ... Ts>
bool createTable(const std::string & tableName, const std::initializer_list<std::string> & columnNames);
And this is the body of the method:
template<typename ... Ts>
bool DatabaseConnection::createTable(const std::string &tableName, const std::initializer_list<std::string> & columnNames)
{
constexpr size_t num_cols = sizeof...(Ts);
assert(num_cols == columnNames.size());
auto typetuple = std::tuple<Ts...>();
std::vector<std::tuple<std::string, std::string>> columnNameAndType(num_cols);
auto columnNameIterator = columnNames.begin();
for(unsigned it = 0; it++ < columnNames.size(); it++){
typedef std::tuple_element<it, typetuple>::type c; // non-type template argument is not a constant expression
if(is_same<c, int> ...) //pseudocode
std::string dbtype = "INTEGER"; //pseudocode
}
}
Sadly, the tuple_element line doesn't work, because it's not really a
constant expression.
Now, someone might ask, why I want to call it like this:
createTable<int, std::string>("Users", {"ID", "Name"});
instead of just passing two initialiser lists?
Well I just want to distance the user from the interface - If I were able to determine
the it-h type I could just use something like decltype or is_same to determine the type used in database creation query - the user just says what type he/she wants and the Database class
determines the best database type to match the user's request.
Now, it could still be made with initaliser lists, but it wouldn't be compile time, and
I'm just curious to see if it's possible at comple time.
I hope my explanation of the problem is sufficient.
Of course this is mostly a theoretical problem, but I think many people
would be interested in such a syntax, and I haven't found any solutions on the internet yet.
This interface is certainly possible.
A for loop isn't going to do it, because one statement/variable/expression/etc. can't have different types on different evaluations of a for substatement. The loop will need to be via pack expansion instead.
One or more private helper member functions could help for this. It would be possible to get it all in one function definition using a generic lambda, but a little unpleasant.
// private static
template <typename T>
std::string DatabaseConnection::dbTypeName()
{
if constexpr (std::is_same_v<T, int>)
return "INTEGER";
// ...
else
static_assert(!std::is_same_v<T,T>, "Unsupported type argument");
}
template<typename ... Ts>
bool DatabaseConnection::createTable(
const std::string &tableName,
std::initializer_list<std::string> columnNames)
{
constexpr size_t num_cols = sizeof...(Ts);
assert(num_cols == columnNames.size());
std::vector<std::tuple<std::string, std::string>> columnNameAndType;
auto columnNameIterator = columnNames.begin();
(columnNameAndType.emplace_back(*columnNameIterator++, dbTypeName<Ts>()), ...);
// ...
}

Complex Use of Template in C++

I come across a piece of code about template:
unroller.h
template <int It, int End> struct unroller {
template <typename Action> static void step(const Action &action) {
action(std::integral_constant<int, It>());
unroller<It + 1, End>::step(action);
}
};
template <int End> struct unroller<End, End> {
template <typename Action> static void step(const Action &) {}
};
snippet of the code that show how it is invoked:
constexpr int NUM_ROWS = 2;
constexpr int NUM_COLS = 2;
int input_idx = 0;
for (int pass = 0; pass < num_passes; pass++) {
unroller<0, NUM_ROWS>::step([&input_idx, input_accessor](auto i_idx) {
constexpr int i = i_idx.value;
unroller<0, NUM_COLS>::step([&input_idx, input_accessor](auto j_idx) {
constexpr int j = j_idx.value;
ProducerToConsumerPipeMatrix::pipe_at<i, j>::write(input_accessor[input_idx++]);
});
});
}
Question:
template <typename Action> static void step(const Action &action) is function template?
what is action(std::integral_constant<int,It>());? I thought action is pass by reference. However, i don't really understand it.
I don't understand the way it invoke unroller<0, NUM_ROWS>::step([&input_idx, input_accessor](autoi_idx). My understanding is in the angle bracket <> should be data types. In this case, it pass the value. Besides, what is ::step? Then, why it can pass 2 argument in the step(...)>
I am new to c++. Can someone explain in simple way?
I think you may need to start learning templates from the beginning.
Yes. It's a typical function template
No, it's passed by value, at least in your example is passed by value.
[&input_idx, input_accessor](auto j_idx) {
constexpr int j = j_idx.value;
ProducerToConsumerPipeMatrix::pipe_at<i, j>::write(input_accessor[input_idx++]);
});
this lambda is passed into higher-order function step as its parameter, it will later receieve a std::integral_constant<int, It> prvalue temporary as the parameter of the lambda.
std::integral_constant<int,It>() constructs a temporary object of std::integral_constant<int, It>, see this: https://en.cppreference.com/w/cpp/types/integral_constant
It's not two arguments, it's lambda capture, see this: https://en.cppreference.com/w/cpp/language/lambda
the parameters of a template don't need to be types, see this: https://en.cppreference.com/w/cpp/language/templates#Syntax
I am new to c++. Can someone explain in simple way?
I'm going to try, but this is template meta-programming that usually needs some kind of expertise mixed with functional style (which is another realm of expertise).
template static void step(const Action &action) is function template?
Yes, a function in which Action is a parameter.
what is action(std::integral_constant());? I thought action is
pass by reference. However, i don't really understand it.
The general idea of unroller is to unroll a loop. This to generate automatically a sequence of calls to something like:
action(0);
action(1);
action(2);
...
action(NUM_ROWS-1);
action is then a parameter to any function that is able to get an int value.
The function used here is:
[&input_idx, input_accessor](auto i_idx) {
constexpr int i = i_idx.value;
unroller<0, NUM_COLS>::step([&input_idx, input_accessor](auto j_idx) {
constexpr int j = j_idx.value;
ProducerToConsumerPipeMatrix::pipe_at<i,j>::write(input_accessor[input_idx++]);
}
}
which itself uses the unrolling! Looks like it unrolls a computation on 2D matrix elements. Theses functions are called lambdas, anonymous functions is you prefer. Then you are in the realm of functional programming.
I don't understand the way it invoke unroller<0, NUM_ROWS>::step([&input_idx, input_accessor](autoi_idx). My
understanding is in the angle bracket <> should be data types. In this
case, it pass the value. Besides, what is ::step? Then, why it can
pass 2 argument in the step(...)>
Template parameters can be types but also values of different kind...
unroller is a template type that takes two arguments: <int It, int End>.
It and End are typed as ints not class or typename (as it is with Action).

metaprogramming with variadic templates in C++

I am working on a simple game engine that provides a base class for game objects that can be extended with subclasses for the specific game. I need to write a function that can take a file, parse object names from it, and instantiate the corresponding objects in the game; providing a mechanism for storing level data in files. I had hoped to use metaprogramming to create a function that allows the caller to pass in a variable number of data types and generates a function that searches for names corresponding to those types in a file. Its use would look something along the lines of this (using templates):
fileParseFunction<type1, type2 type3>("filename");
would generate a function equivalent to:
fileParseFunction(string filename)
{
//code that opens file called "filename" and handles tokenizing/parsing
if(token == "type1")
{
gameWorld.add(new type1());
}
elseif(token == "type2")
{
gameWorld.add(new type2());
}
elseif(token == "type3")
{
gameWorld.add(new type3());
}
//other code to finish loading the level
}
Called with the parameter "filename". This should work for a variable number of types (3 in the example). I wrote some code to test the concept which involves generating a similar function. It uses templates to convert typename symbols to strings (this is needed for use in the comparisons in the function I eventually hope to write) and also variadic templates to generate a function that prints the names of all the types passed in as template parameters. Here it is:
#define TypeNameTemplate(a) template<> inline const char* typeName<a>(void) { return #a; }
template <typename T>
inline const char* typeName(void) { return "unknown"; }
TypeNameTemplate(int);
TypeNameTemplate(std::string);
TypeNameTemplate(double);
TypeNameTemplate(bool);
TypeNameTemplate(float);
/*template <>
inline const char* typeName<int>(void) { return "int"; }*/
template <typename T> inline void printtypes()
{
std::cout << typeName<T>();
}
template <typename T, typename... Args> void printtypes()
{
std::cout << typeName<T>() << std::endl;
printtypes<Args...>();
}
using namespace std;
int main()
{
//string a = typeName<int>();
//print();
printtypes<int, double, string, bool, float>();
return 0;
}
printtypes() should generate a function equivalent to:
void printtypes()
{
std::cout << typeName<int>();
std::cout << typeName<std:string>();
std::cout << typeName<double>();
std::cout << typeName<bool>();
std::cout << typeName<float>();
}
However, during compilation i get this error:
E:\C++ projects\templateTest\main.cpp:26:5: note: candidates are:
E:\C++ projects\templateTest\main.cpp:18:35: note: void printtypes() [with T = float]
E:\C++ projects\templateTest\main.cpp:23:46: note: void printtypes() [with T = float; Args = {}]
It appears that upon reaching the end up the variadic parameter pack recursively, the compiler does not know whether to call the template specialized on only one type with the last type in the pack, or the variadic template with the last type in the pack plus an empty parameter pack. Is what I'm attempting to do possible/practical in C++, and is there a way to let the compiler know that it should use the singe parameter template for the base/final case of the recursive call?
A simple solution is to add another explicit parameter to the second overload:
template <typename T, typename T2, typename... Args> void printtypes()
{
std::cout << typeName<T>() << std::endl;
printtypes<T2,Args...>();
}
While this answer does not answer your specific question about variadic templates, I hope it does answer your underlying question about how to make your game engine extensible.
What you came up with by creating your fileParseFunction() is an implementation of the Factory Method pattern. This is the main part that makes it easy to turn stored data into real objects. Unfortunately, it violates the Open-Close Principle making it difficult to reach your end goal, extendability.
For example, in your code above, your factory function can parse "type1", "type2" and "type3" from your data file and generate objects of type1, type2 and type3 but adding more types would mean editing this function and adding a new else if for every new type you wish to add
You've already identified this as a problem and are trying to solve it using variadic templates. Unfortunately, if you extend the number of game objects into the twenties, thirties, or even hundreds of types, variadic templates will become cumbersome to use, if at all they are able to go that far.
A much simpler solution is to use the Abstract Factory pattern. This essentially shifts responsibility for creating game objects from your file parser's Factory Function, to a factory object. Whether this transfer of power goes to a single function, or a fully-fledged class is up to you. You could also templatise this factory to save on coding.
Each of your factories will have to register their existence with the file parser before the parser is called and extending the parser's capabilities will be as simple as creating a new factory and registering it with the parser.
A simple example would be:
class GameObjectAbstractFactory {
public:
string registeredTypes() const{
// cycle through hash table to return the names of registered factories
}
GameObjectFactory* getFactory(string factoryName){
// return the registered factory, or nullptr if not registered
}
void registerFactory(string factoryName, GameObjectFactory*){
// add the factory if it doesn't exist
}
static GameObjectAbstractFactory* instance(){
// access to Singleton instance
}
private:
GameObjectAbstractFactory(); // enforces Singleton pattern
Hash<string, GameObjectFactory*> registeredFactories;
};
// Interface class for generating concrete types, can be templatised, depending on implementation
class GameObjectFactory{
public:
string name() = 0;
GameObject *createObject() = 0;
};
This would alter your parsing function so that it becomes:
fileParseFunction(string filename)
{
//code that opens file called "filename" and handles tokenizing/parsing
GameObjectAbstractFactory *abstractFactory = GameObjectAbstractFactory::instance();
GameObjectFactory *factory = abstractFactory.getFactory(token);
if(factory != nullptr)
{
gameWorld.add(factory.createObject());
}
//other code to finish loading the level
}
This would then make your fileParseFunction() compliant with the Open-Close Principle in that it could still generate new game objects as your engine is extended but the function itself will not have to be modified in order to do so.
There is a caveat with this pattern though: all the factories need to be registered with the abstract factory before they are needed, otherwise the required game object will not be able to be created.
As I mentioned in the beginning, this answer does not address your direct question about variadic templates but I hope this helps with the extendability of your game engine.

Why isn't my boost::multi_index modify() compiling?

I'm using boost::multi_index_container and am trying to get a modify operation working. My modification routine looks like this (roughly), using a function inner class:
void FooContainer::modifyAttribute(string key, int newValue) {
struct ModifyFunc {
int val;
ModifyFunc(int val): val(val) {}
void operator()(Foo &f) {
foo.val = val;
}
};
StorageContainer::index<keyTag>::type &idx = mContainer.get<keyTag>();
StorageContainer::index<keyTag>::type::iterator iter = idx.find(key);
idx.modify(iter, ModifyFunc(newValue));
}
When I try to compile this, I get a multi-page spew of compiler error like this (most of it omitted):
FooContainer.cpp:##: error: no matching function for call to [...]::modify([...]&, FooContainer::modifyAttribute(string,int)::ModifyFunc)’
What's wrong with this invocation and how can I make it work?
The problem is that function inner classes aren't recognized by the compiler as a valid typename for template parameters; it isn't obvious, but the multi_index_container::index<T>::type::modify method uses the type of the modify parameter as a template argument which is a detail normally hidden from view. However, look at its declaration in, for example, boost/multi_index/hashed_index.hpp:
template<typename Modifier>
bool modify(iterator position,Modifier mod)
The easy fix is to make the ModifyFunc struct not a function inner class; trivially, make it inner to FooContainer rather than to the FooContainer::modifyAttribute method. Of course, this also means that you can reuse the ModifyFunc class elsewhere, if it turns out you need it in more than one place.