MarcusAseth

More Template questions

Recommended Posts

 

I start by saying that I am aware that what I am trying to do can easily be achieved trough the <functional> part of the library or trough a Functor or a Lambda, but I wanted to see this in template form.(Code below)

So the first function works, the find_if algorithm find the first value in a vector greater than the specified parameter, but there is no template argument deduction for that function call because the algorithm require a pointer to a function but at that time it is not know I will pass an int into it, and so I need to specify like this:

LargerThan_NoDeduction<30,int>

But this seems ugly because now I have to take care of match the two, like <31.2, double>, and the worst part is that if I now decide to pass something else, like a <'d',char> or a <-10,float> , the function expects a size_t as first template parameter, so this won't do.

So what I wanted to achieve was to pass a predicate to an algorithm in the form of

LargerThan(30)

where the template part of it takes care both of storing the data value (in this case 30, but could be a 'c') and deducing the type we compare from out of it, so in this case int.

So I have a function LargerThan(Type) that returns a function pointer and passes down the value to an helper function which takes both the value and the deduced type, so I don't have to type them myself.

Problem is, this helper function still has an auto in the first template parameter, and the compiler doesn't like this xD

How would you make this work trough template magics? :P

#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;

////
template<size_t TestCase,typename Type>
bool LargerThan_NoDeduction(Type value)
{
	return value > TestCase;
}

////
template<typename Type>
auto LargerThan(Type TestCase)-> bool(*)(Type)
{
	return LargerThan_helper<TestCase, Type>;
}

template<auto TestCase, typename Type> //auto here is not liked!!!
bool LargerThan_helper(Type value)
{
	return value > TestCase;
}
////

int main()
{
	vector<int> vec{ 0,11,21,35,67,102 };

	//Must specify size and type.
	auto p = find_if(vec.begin(), vec.end(), LargerThan_NoDeduction<30,int>);//WORKS
	if (p != vec.end()) { cout << *p << endl; }//WORKS

	//Deduces type from the value passed.
	p = find_if(vec.begin(), vec.end(), LargerThan(30));//ERROR
	if (p != vec.end()) { cout << *p << endl; }//ERROR

	return 0;
}

 

Edited by MarcusAseth

Share this post


Link to post
Share on other sites

Function pointers cannot store extra information. So use something that can:

template<typename Type>
struct LargerThanHelper {
    LargerThanHelper(Type value)
        : m_value(value) {
    }

    bool operator()(Type other) {
        return other > m_value;
    }

private:
    Type m_value;
};

template<typename Type>
LargerThanHelper<Type> LargerThan(Type TestCase) {
    return LargerThanHelper<Type>(TestCase);
}

 

Share this post


Link to post
Share on other sites

So a functor is indeed needed :P

What about static local variables though? I wonder if one can use it to store the value data inside the innermost function and achieve the same without a functor.

By the way, I totally have no reason to not use functors, I am simply exploring what can be done with templates functions alone :) 

Edited by MarcusAseth

Share this post


Link to post
Share on other sites

I found a very useless way to have it work without a functor! :D

Basically the first time it gets instantiated, the TestCase is set and stored inside of it.

Problem is, it can be set only once when instantiated, all the future usage will use that TestCase, pretty useless x_x

Can we improve on it? :P

#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;

////
template<typename Type>
auto LargerThan(Type TestCase)-> bool(*)(Type)
{
	bool(*func)(Type) = LargerThan_helper<Type>;
	func(TestCase);
	return func;
}

template<typename Type>
bool LargerThan_helper(Type value)
{
	static bool set = false;
	static Type TestCase;
	if (!set) { TestCase = value; set = true; }
	return value > TestCase;
}
////

int main()
{
	vector<int> vec{ 0,11,21,35,67,102 };

	auto p = find_if(vec.begin(), vec.end(), LargerThan(30)); //p points to 35
	if (p != vec.end()) { cout << *p << endl; }

	return 0;
}

 

Share this post


Link to post
Share on other sites

Man, I thought this time I had it! >_>

There must be a logical error.

My plan was to leverage the overload resolution, so that LargerThan() would call LargerThan_helper() with 2 arguments, and when called with 2 argumens the Unpack function overload would set the static variable used for comparison  to a new value.

All the other time the algorithm would call the LargerThan_helper() which I had passed it trough a pointer, and since the algorithm only passes one value, the other Unpack overload would be called and testCase won't get modified.

I know this is still BS because this now rely on an algorithm passing only 1 value, regardless, what is my logic error in this case?

Why find_if is returning a pointer to 22 rather than 67? Can anyone see the cause? :S 

By the way don't sweat it, I am only fooling around, probably it doesn't matter if this stays an unsolved mistery, though would be interesting to know :P 

EDIT: wait... could it be that by calling it with 2 arguments I am getting a different instantiation than when called with one (I was assuming that the <typename...Args> would count as a single thing, but probably doesnt)?! If that was the case, then the TestCase = 66; would be in the other instantiation, and when the algorithm calls it with 1 value, then the static variable would probably be 0, and the first value in the vector is greater than 0 so I get that as a positive... is that the case? Seems possible, since if I set the first value of the vector to -22, now I get returned the second value... x_x Maybe I'll give up :P 

#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;

////
template<typename Type>
auto LargerThan(Type TestCase)-> bool(*)(Type)
{
	//2 argument are passed in order to call the overload that sets TestCase
	LargerThan_helper(TestCase, TestCase);
	
	return LargerThan_helper;
}

template<typename... Args>
bool LargerThan_helper(Args... args)
{
	//set TestCase to the type of the first argument
	static decltype(Type(args...)) TestCase;

	//if 3 argument passed, overload resolution calls
	//the function that set TestCase, if 2 argument are passed then
	//only the value for the comparison is returned
	auto value = Unpack(TestCase, args...);
	return value > TestCase;
}

template<typename First,typename... Rest>
First Type(First f,Rest... r) { return f; }

template<typename Type>
Type Unpack(Type& testCase, Type first) { return first; }

template<typename Type>
Type Unpack(Type& testCase, Type first, Type second) { testCase = second; return first; }


int main()
{
	vector<int> vec{ 22,11,21,35,67,102 };

	auto p = find_if(vec.begin(), vec.end(), LargerThan(66));//WRONG RESULT, returns pointer to 22
	if (p != vec.end()) { cout << *p << endl; }

	return 0;
}

 

Edited by MarcusAseth

Share this post


Link to post
Share on other sites

The major problem with your approach is that you are trying to call into the entry point of this API with a runtime value ("LargerThan(30)"). You cannot transport a runtime value (the parameter 30) to a compile-time constant. The value doesn't exist at compile time.

You can make LargerThan<33>() work:

typedef bool (*Comparison)(int)
template<int Constant>
bool LargerThanImpl(std::size_t other) {
  return other > Constant;
}
  
template<int Constant>
Comparison LargerThan() {
  return LargerThanImpl<Constant>;
}

But you cannot deduce a non-type argument from a type argument in a way that would be useful for this until C++17. After C++17, you can use the method detailed in that link, but non-type parameters can only be integers (and a few other things that are irrelevant here), which means you can't work with floats this way.

Give up this static-based approach; it's not a sane solution to the problem; if you really want to accept insane solutions that involve contortions that render the solution unusable in modern, real-world practice, that's your business, but it's a huge waste of time.

Share this post


Link to post
Share on other sites
1 hour ago, MarcusAseth said:

EDIT: wait... could it be that by calling it with 2 arguments I am getting a different instantiation

Yes. Templates create a family of functions, and each instantiation is basically as unique as if you wrote it out by hand. So that static is not shared between every member of that family. Every member of that family gets its own static, which is (part of) what makes that approach entirely unworkable.

Functions in C++ do not have state, except what state you can encode in their signature (via type meta programming and non-type arguments). But transporting information back in time from runtime to compile-time is impossible.

Share this post


Link to post
Share on other sites
4 hours ago, jpetrie said:

Give up this static-based approach; it's not a sane solution to the problem; if you really want to accept insane solutions that involve contortions that render the solution unusable in modern, real-world practice, that's your business, but it's a huge waste of time.

Already gave up, never though for a second this was better than using a Function object in any way, just exploring! xD

Also thanks for all the explanations @jpetrie, really nice to see that template<auto value> coming! I bet eventually it will work with floats too :P

Edited by MarcusAseth

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


  • Partner Spotlight

  • Forum Statistics

    • Total Topics
      627637
    • Total Posts
      2978335
  • Similar Content

    • By irreversible
      I'm writing some code for automatic function argument type deduction using a tuple. I'm then iterating over it to narrow down and check each argument separately. I spent a cozy evening tinkering with getting by-value, const value and const references to work, until I discovered that some functions need to return more than one result. This is where I need to identify non-const lvalue references, which a tuple has difficulty in handling.
      As far as I can tell most problems out and about on the web focus on creating fairly simple stand-alone tuples that only contain lvalue references using std::tie. In particular, this stackexchange thread outlines how that can be accomplished. 
      Problem is, I have a packed array of types, which may or may not contain one or more lvalue references interleaved with other types. forward_as_tuple is suggested here and there, but I'm unsure how to use it. 
      Here's there relevant code:
      // split the return type from the argument list template<typename R, typename... Args> struct signature<R(Args...)> { using return_type = R; using argument_type = std::tuple<Args...>; // 1 }; template<typename FUNCSIG> editor::nodetype_t& CreateNodeType( IN const char* category) { // instantiate the argument list tuple. No need to post any further code as this // is where things fail to compile signature<FUNCSIG>::argument_type arglist; // 2 } // the below snippet outlines how CreateNodeType() is called: #define DEFINE_MTL_NODE(function, category, ...) \ auto& nodeType = CreateNodeType<decltype(function)>(category); // a sample function for completeness. I'm intentionally not using the return value here. void Lerp( IN const math::vec3& ColorIn1, IN const math::vec3& ColorIn2, IN float Alpha, OUT math::vec3& ColorOut) { .. } void main() { DEFINE_MTL_NODE(Lerp, "Color"); } Either the line marked with 1 or 2 needs to be something else, but apparently my C++ level is not high enough to figure out what. PS - to further complicate things, I'm stuck on C++11 for now.
      Ideas?
    • By noodleBowl
      I got a quick question about buffers when it comes to DirectX 11. If I bind a buffer using a command like:
      IASetVertexBuffers IASetIndexBuffer VSSetConstantBuffers PSSetConstantBuffers  and then later on I update that bound buffer's data using commands like Map/Unmap or any of the other update commands.
      Do I need to rebind the buffer again in order for my update to take effect? If I dont rebind is that really bad as in I get a performance hit? My thought process behind this is that if the buffer is already bound why do I need to rebind it? I'm using that same buffer it is just different data
       
    • By noodleBowl
      When it comes to the copy, move, and assignment operators of a child class how is it supposed to look?
      If this is the implementation of my parent class
      //Default constructor Buffer::Buffer() { buffer = nullptr; } //Copy constructor Buffer::Buffer(const Buffer& other) { buffer = other.buffer; buffer->AddRef(); } //Move constructor Buffer::Buffer(Buffer&& other) { buffer = other.buffer; buffer->AddRef(); other.release(); //Free the buffer resource of the other instance } //Destructor Buffer::~Buffer() { release(); } //Copy assignment Buffer& Buffer::operator=(const Buffer& other) { if (&other != this) { release(); //Free the buffer of this instance buffer = other.buffer; buffer->AddRef(); } return *this; } //Move assignment Buffer& Buffer::operator=(Buffer&& other) { if (&other != this) { release(); //Free the buffer of this instance buffer = other.buffer; buffer->AddRef(); other.release(); //Free the buffer of the other instance } return *this; } And this is the implementation of my child class. Is this correct?
      I'm really just wondering about the at the copy, move, and I'm not too sure about the assignment operators
      //Default constructor VertexBuffer::VertexBuffer() : Buffer() { } //Copt constructor VertexBuffer::VertexBuffer(const VertexBuffer& other) : Buffer(other) { } //Move constructor VertexBuffer::VertexBuffer(VertexBuffer&& other) : Buffer(other) { } //Destructor VertexBuffer::~VertexBuffer() { } //Not sure how to handle the copy/move operators. //Do I treat them as there own operators and copy the Buffer assignment operators code? VertexBuffer& VertexBuffer::operator=(const VertexBuffer& other) { } VertexBuffer& VertexBuffer::operator=(VertexBuffer&& other) { }  
    • By SeraphLance
      After seeing this talk (and combined with similar experiences in other languages), I got to thinking just how many times I pointlessly copy variables around.  I'm sure it's a lot, and I'm sure more experienced C++ programmers are better at catching these things (especially given how off-handed he remarks these -- like they're "duh" to everyone in the room), but it feels like I'm a bit lost as to how to actually find these sorts of mistakes.  Right now it sort of feels like an "experience through code review" thing, and that's a bit disconcerting.  Are there tools or techniques to track this sort of thing, so that I can find whether I'm spuriously making copies where I didn't intend to?
      In context, the very first example he gives is the sort of thing I'd do if I thought I was being clever.
    • By Estra
      Memory Trees is a PC game and Life+Farming simulation game. Harvest Moon and Rune Factory , the game will be quite big. I believe that this will take a long time to finish
      Looking for
      Programmer
      1 experience using Unity/C++
      2 have a portfolio of Programmer
      3 have responsibility + Time Management
      and friendly easy working with others Programmer willing to use Skype for communication with team please E-mail me if you're interested
      Animator
      We already have 3dModel just need animator for make rig 3DModel and animation in game
      MemoryTrees RPG Life+Farming simulation game base on runefactory and harvestmoon

      1 Can make Rig and adjust the rig avoids,animator must give some feedback to 3D artist when he rigging the 3D model then 3D artist could adjust the 3D model to fit the rig as match as possible
      ( Our game have mermaid/Centaur/ Fantasy character ect. )

      2 need to know Blender, Maya or any other 3D tool

      3 know how to import/export to .fbx files, .blend files or any file Unity can import.

      4 It would be great if the you showed us some example work so we can have an idea

      5 have responsibility
      Split %: Revenue share. We can discuss. Fully Funded servers and contents
      and friendly easy working with others willing to use Skype for communication with team please E-mail me if you're interested
      we can talk more detail in Estherfanworld@gmail.com Don't comment here
      Thank you so much for reading
      More about our game
      Memory Trees : forget me not


      Thank you so much for reading

      More about our game
      Memory Trees : forget me not

  • Popular Now