• Announcements

    • khawk

      Download the Game Design and Indie Game Marketing Freebook   07/19/17

      GameDev.net and CRC Press have teamed up to bring a free ebook of content curated from top titles published by CRC Press. The freebook, Practices of Game Design & Indie Game Marketing, includes chapters from The Art of Game Design: A Book of Lenses, A Practical Guide to Indie Game Marketing, and An Architectural Approach to Level Design. The GameDev.net FreeBook is relevant to game designers, developers, and those interested in learning more about the challenges in game development. We know game development can be a tough discipline and business, so we picked several chapters from CRC Press titles that we thought would be of interest to you, the GameDev.net audience, in your journey to design, develop, and market your next game. The free ebook is available through CRC Press by clicking here. The Curated Books The Art of Game Design: A Book of Lenses, Second Edition, by Jesse Schell Presents 100+ sets of questions, or different lenses, for viewing a game’s design, encompassing diverse fields such as psychology, architecture, music, film, software engineering, theme park design, mathematics, anthropology, and more. Written by one of the world's top game designers, this book describes the deepest and most fundamental principles of game design, demonstrating how tactics used in board, card, and athletic games also work in video games. It provides practical instruction on creating world-class games that will be played again and again. View it here. A Practical Guide to Indie Game Marketing, by Joel Dreskin Marketing is an essential but too frequently overlooked or minimized component of the release plan for indie games. A Practical Guide to Indie Game Marketing provides you with the tools needed to build visibility and sell your indie games. With special focus on those developers with small budgets and limited staff and resources, this book is packed with tangible recommendations and techniques that you can put to use immediately. As a seasoned professional of the indie game arena, author Joel Dreskin gives you insight into practical, real-world experiences of marketing numerous successful games and also provides stories of the failures. View it here. An Architectural Approach to Level Design This is one of the first books to integrate architectural and spatial design theory with the field of level design. The book presents architectural techniques and theories for level designers to use in their own work. It connects architecture and level design in different ways that address the practical elements of how designers construct space and the experiential elements of how and why humans interact with this space. Throughout the text, readers learn skills for spatial layout, evoking emotion through gamespaces, and creating better levels through architectural theory. View it here. Learn more and download the ebook by clicking here. Did you know? GameDev.net and CRC Press also recently teamed up to bring GDNet+ Members up to a 20% discount on all CRC Press books. Learn more about this and other benefits here.
Sign in to follow this  
Followers 0
Juliean

Passing function template pointer

6 posts in this topic

Hello,

 

I want to be able to pass a pointer to a templated function as a paramter of another function, without specifying the concret template type. From what I've read, this is not possible per-se, however since my case is very specific, I belive that there is at least a work-around. This is how it should syntactically work and look like:

// template function to be passed & called
template<typename Type>
void appendAttribute(ICustomComponentAttributes::AttributeVector& vAttributes, const xml::Node& value)
{
    vAttributes.emplace_back(value.GetName(), conv::FromString<Type>(value.GetValue()));
}

// this is the receiving function/method
template<typename Function, typename Return, typename... Args> // ???
Return AttributeDeclaration::CallByType(Args&&... args) const
{
    switch(type)
    {
    case AttributeType::BOOL:
        return Function<bool>(args...);
    case AttributeType::FLOAT:
        return Function<float>(args...);
    case AttributeType::INT:
        return Function<int>(args...);
    case AttributeType::STRING:
        return Function<std::wstring>(args...);
    default:
        ACL_ASSERT(false);
    }
}

// in code

attribute.CallByType<appendAttribute>(vAttributes, node);

So as you can see, what I already try is to pass the function as part of a template, which of course didn't work that easily. I searched around a bit and found template template arguments, which unfortunately didn't seem to work for my example.

 

Is there a way to get this to work with a similar way to what I try to achieve with template arguments here? I belive that there is, since technically the template instantiations are known inside the CallByType-method. If there isn't, is there any workaround? Code complexity is of no concern, but it should be as easy to use in the actual code as possible, since this will be used in many instances. Thanks!

 

 

 

 

 

 

 

 

0

Share this post


Link to post
Share on other sites
Why does CallByType have to accept appendAttribute as a template parameter?

The usual way of doing this kind of thing in C++ is to pass the appendAttribute function as a regular function parameter into CallByType, which then invokes it via () syntax. This can be templated to allow for arbitrary functions, function objects, and so on to be passed into CallByType.
2

Share this post


Link to post
Share on other sites


Why does CallByType have to accept appendAttribute as a template parameter?

 

I just used a template parameter because I believed that this kind of stuff could not be done by passing the function template as a function parameter. Please by any means correct me if I'm wrong but there is no such thing as:

void function(void (*func)<typename type>(void))
{
}

Or is there? The thing is that I don't explicitely want to instantiate the templated function in the user code. I want the CallByType-method to actually create the instantiations... which, in my limited knowledge of the inner working of things here lead to the assumption that this could only be done by passing the function as the template itself.

 

If it works the way you described it, could you give me a short (pseudo)code example of how I would use it in that way? I can't quite wrap my head around it :/

0

Share this post


Link to post
Share on other sites
Think of it this way: your function template as posted will generate overloads of the function appendAttribute, each with the same function signature but a different body. To wit, the function signature is:

void foo(ICustomComponentAttributes::AttributeVector&, const xml::Node&)
Now, part two of the equation:

template <typename FType>
void InvokeFunction (FType func) {
    func(42);
}
Calling InvokeFunction(foo); for any function foo (well, as long as it accepts something convertible from int) will call through to foo with the parameter 42.

Since the appendAttribute function signature is known, you can do the same trick. Consider this minimal program:

#include <iostream>

template <typename T>
void Invoked (int param) {
    std::cout << param << std::endl;
}

template <typename FType>
void Invoker (FType func) {
    func(42);
}

int main () {
    Invoker(Invoked<int>);
}
I'm not entirely clear on what you want your attribute append code to look like, but those are the building blocks; hopefully that gets you to a solution, but if not, if you could clarify your intent a bit I'd be happy to try and piece together something.
2

Share this post


Link to post
Share on other sites

I'm not entirely clear on what you want your attribute append code to look like, but those are the building blocks; hopefully that gets you to a solution, but if not, if you could clarify your intent a bit I'd be happy to try and piece together something.

 

I quess it needs a little more explanation. I always try to keep things as simple as possible when explaining, but I quess I failed here.

 

So basically I want to simplify/encapsulate one specific instance of code that I'm using quite often in a specific module. In that module, there is a class called "AttributeDeclaration" which holds a type-enumerator.

enum class AttributeType
{
    INT, FLOAT, BOOL, STRING
}

Now in specific code, I have to switch based on this attribute-type in order to work on some data based on the AttributeDeclaration, which on first iteration would look like this:

void Loader::InitCustomAttribute(const xml::NodeVector& vNodes, ICustomComponentAttributes& custom)
{
	ICustomComponentAttributes::AttributeMap mAttributes;
	mAttributes.reserve(vNodes.size());

	for(auto pValue : vNodes)
	{
		const auto type = (AttributeType)pValue->Attribute(L"type")->AsInt();
		
		// here, we switch on the type and convert the string to a specific known "POD" type.
		switch(type)
		{
		case AttributeType::BOOL:
			mAttributes.emplace(value.GetName(), conv::FromString<bool>(value.GetValue()));
			break;
		case AttributeType::INT:
			mAttributes.emplace(value.GetName(), conv::FromString<int>(value.GetValue()));
			break;
		case AttributeType::FLOAT:
			mAttributes.emplace(value.GetName(), conv::FromString<float>(value.GetValue()));
			break;
		case AttributeType::STRING:
			mAttributes.emplace(value.GetName(), conv::FromString<std::wstring>(value.GetValue()));
			break;
		}
	}

	custom.OnSetAttributes(*pComponent, mAttributes);
}

Note that there is a good amount of dublicated code in the switch-case statements, which only really differs by the template-argument for the FromString-function (lets pretend for the sake of simplicity that conv::FromString<std::wstring> was a real thing). So it appears natural to make a template helper function:

template<typename Type>
void appendAttribute(ICustomComponentAttributes::AttributeMap& mAttributes, const xml::Node& value)
{
	mAttributes.emplace(value.GetName(), conv::FromString<Type>(value.GetValue()));
}

void Loader::InitCustomAttribute(const xml::NodeVector& vNodes, ICustomComponentAttributes& custom)
{
	ICustomComponentAttributes::AttributeMap mAttributes;
	mAttributes.reserve(vNodes.size());

	for(auto pValue : vNodes)
	{
		const auto type = (AttributeType)pValue->Attribute(L"type")->AsInt();

                // now at least the implementation of each switch-block is unified
		switch(type)
		{
		case AttributeType::BOOL:
			appendAttribute<bool>(mAttributes, *pValue);
			break;
		case AttributeType::INT:
			appendAttribute<int>(mAttributes, *pValue);
			break;
		case AttributeType::FLOAT:
			appendAttribute<float>(mAttributes, *pValue);
			break;
		case AttributeType::STRING:
			appendAttribute<std::wstring>(mAttributes, *pValue);
			break;
		}
	}

	custom.OnSetAttributes(*pComponent, mAttributes);
}

Well, now I have the implementation for each data-type shared. Thats the way it looks almost everywhere I "have to" (see at the end of the post) switch on the type. Still, I always have to write the switch-statement per hand for every usage of the attribute-declaration, which causes

 

- dublicated code

- loss in readability due to increased code length (subjective)

- prone to errors (more than once did I use <float> on AttributeType::INT)

 

And thats where my original question kicks in. Thats also why your example isn't usable for me (unless I`m mistaken in comprehending it), because

Invoker(Invoked<int>);

I can`t/won't call my appendAttribute template-function with the specific type in the "user" code. It should essentially look like this:

// how exactly is this declared?
// for sake of clarity, attributes are hard-coded for the appendAttribute-function here
void CallByType(AttributeType type, ICustomComponentAttributes::AttributeMap& mAttributes, const xml::Node& value)
{
	// we moved the switch inside of this function - now its unified & encapsulated,
	// all the user needs to know is that the correct overload of the template function will be
	// called
	switch(type)
	{
	case AttributeType::BOOL:
		inFunction<bool>(mAttributes, *pValue);
		break;
	case AttributeType::INT:
		inFunction<int>(mAttributes, *pValue);
		break;
	case AttributeType::FLOAT:
		inFunction<float>(mAttributes, *pValue);
		break;
	case AttributeType::STRING:
		inFunction<std::wstring>(mAttributes, *pValue);
		break;
	}
}

void Loader::InitCustomAttribute(const xml::NodeVector& vNodes, ICustomComponentAttributes& custom)
{
	ICustomComponentAttributes::AttributeMap mAttributes;
	mAttributes.reserve(vNodes.size());

	for(auto pValue : vNodes)
	{
		const auto type = (AttributeType)pValue->Attribute(L"type")->AsInt();
		
		// now we got rid of the switch-statement entirely.
		CallByType<appendAttribute>(type, mAttributes, *pValue);
	}

	custom.OnSetAttributes(*pComponent, mAttributes);
}

So the CallByType-function should execute this one very template functions correct overload based on the runtime-resolved variable. Which means it has to instantiate all 4 template declarations - but thats part of what this function should do, not what I want to do everytime I use the CallByType-function.

 

Then, whenever I have to execute code that appends on an AttributeType-variable, I can call this function with a template overload, which will save me a lot of unnecessary code dublication.

 

Did I manage to get the idea across more clearly this time? If so, I'm glad to hear any suggestion you might have, otherwise please let me know again.

 

PS: Regarding the whole switch-statement per se. I get the feeling your first instinct is calling "use polymorphism, dumbass". I just wanted to add that thats not a real option here for various reasons. The range of operations that are executed depending on such a type-enum is way to differential as for me to be able to put it in one, or even a few well-defined classes. So before I have to create a factory/interface/4 implementations for every such simple template-helper functions, I much prefer the template function with addition of the switch-helper I proposed. You might suggest that based on that the whole system is flawed, but I'm actually really OK with how it works out, minus having to type that switch-statement over and over. Well, thats what this thread is hopefully going to solve.

Edited by Juliean
0

Share this post


Link to post
Share on other sites
This is a really simple mockup of how you could proceed:

#include "stdafx.h"
#include <iostream>
#include <string>

enum Type {
    TYPE_INT,
    TYPE_STRING,
    TYPE_FLOAT,
};


void Printer (int value) {
    std::cout << "INT: " << value << std::endl;
}

void Printer (const std::string & value) {
    std::cout << "STR: " << value.c_str() << std::endl;
}

void Printer (float value) {
    std::cout << "FLT: " << value << std::endl;
}


template <typename T>
struct PrinterWrapper {
    void operator () (void * valuePtr) {
        Printer(*reinterpret_cast<const T *>(valuePtr));
    }
};


template <template <typename FuncParamT> class FuncT>
void InvokeByType (Type t, void * valuePtr) {
    switch (t) {
    case TYPE_INT:
        FuncT<int>()(valuePtr);
        break;

    case TYPE_STRING:
        FuncT<std::string>()(valuePtr);
        break;

    case TYPE_FLOAT:
        FuncT<float>()(valuePtr);
        break;
    }
}


int main () {
    int intValue = 42;
    std::string strValue = "Test";
    float fltValue = 3.1415f;

    InvokeByType<PrinterWrapper>(TYPE_INT, &intValue);
    InvokeByType<PrinterWrapper>(TYPE_STRING, &strValue);
    InvokeByType<PrinterWrapper>(TYPE_FLOAT, &fltValue);
}
Obviously the void* is kinda lame, but you can easily replace that with passing through your XML node and using appropriate parser functions to go from the node attributes to the target types.

I *think* that solves your situation :-)
1

Share this post


Link to post
Share on other sites

This is a really simple mockup of how you could proceed:

 

Ah, I see, using a class to "pass" the function in, clever. That does what I need, but I think I can make it even a little simplier or just my needs:

class getStringFromOffset
{
public:
    template<typename Type>
    static std::wstring Call(const BaseComponent& component, size_t offset)
    {
        return conv::ToString(*(const Type*)((const char*)(&component) + offset));
    }
};

template<typename Functor, typename Return, typename... Args>
Return callByType(AttributeType type, Args&&... args)
{
    switch(type)
    {
    case AttributeType::BOOL:
        return Functor::Call<bool>(args...);
    case AttributeType::FLOAT:
        return Functor::Call<float>(args...);
    case AttributeType::INT:
        return Functor::Call<int>(args...);
    case AttributeType::STRING:
        return Functor::Call<std::wstring>(args...);
    default:
        ACL_ASSERT(false);
    }
}

std::wstring attributeToString(const AttributeDeclaration& attribute, const BaseComponent& component)
{
    return attribute.CallByType<getStringFromOffset, std::wstring>(component, attribute.offset);
}

While also making the call more generic by using templates for return & args. Having such a void* would also kind of go against the principle of what I'm trying to achieve, since contrary to your example the correct TYPE will always be determined by a variable and therefore I won't have any TYPE-specific stuff I have to pass in - thats actually the function/classes task, like you said by e.g. parsing an XML-node to the correct data format.

 

So the only thing that kind of annoying is having to create a class for every function I have to use that way. Do you maybe know any way to simplify this even further and probably allow me to skip the whole class declaration thing? It can even make he callByType-function horribly cluttered, as long as declaring & calling it is as easy as possible ;) I already tried doing a specific macro, but that looked just horrible (additionally to how much I hate macros).

#define TYPE_FUNCTOR(name, ret, args, body)	\
struct name							\
{									\
	template<typename Type>			\
	static ret Call args			\
	{								\
		body						\
	}								\
};									\

TYPE_FUNCTOR(getStringFromOffset, std::wstring, (const BaseComponent& component, size_t offset),
	return conv::ToString(*(const Type*)((const char*)(&component) + offset));
)

So, if you have any idea how to maybe make this work still by only declaring the function w/o that class declaration, it would be really cool - maybe some kind of helper-class template magic? Don't know, if there isn't any way, its still workable, just a little inconvience for me. Thanks regardless, helped me a lot already ;)

Edited by Juliean
0

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!


Register a new account

Sign in

Already have an account? Sign in here.


Sign In Now
Sign in to follow this  
Followers 0