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


  • Forum Statistics

    • Total Topics
      628663
    • Total Posts
      2984111
  • Similar Content

    • By lawnjelly
      It comes that time again when I try and get my PC build working on Android via Android Studio. All was going swimmingly, it ran in the emulator fine, but on my first actual test device (Google Nexus 7 2012 tablet (32 bit ARM Cortex-A9, ARM v7A architecture)) I was getting a 'SIGBUS illegal alignment' crash.
      My little research has indicated that while x86 is fine with loading 16 / 32 / 64 bit values from any byte address in memory, the earlier ARM chips may need data to be aligned to the data size. This isn't a massive problem, and I see the reason for it (probably faster, like SIMD aligned loads, and simpler for the CPU). I probably have quite a few of these, particular in my own byte packed file formats. I can adjust the exporter / formats so that they are using the required alignment.
      Just to confirm, if anyone knows this, is it all 16 / 32 / 64 bit accesses that need to be data size aligned on early android devices? Or e.g. just 64 bit size access? 
      And is there any easy way to get the compiler to spit out some kind of useful information as to the alignment of each member of a struct / class, so I can quickly pin down the culprits?
      The ARM docs (http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.faqs/ka15414.html) suggest another alternative is using a __packed qualifier. Anyone used this, is this practical?
    • By Josheir
      In the following code:

       
      Point p = a[1]; center of rotation for (int i = 0; I<4; i++) { int x = a[i].x - p.x; int y = a[i].y - p.y; a[i].x = y + p.x; a[i].y = - x + p.y; }  
      I am understanding that a 90 degree shift results in a change like:   
      xNew = -y
      yNew = x
       
      Could someone please explain how the two additions and subtractions of the p.x and p.y works?
       
      Thank you,
      Josheir
    • By alex1997
      Hey, I've a minor problem that prevents me from moving forward with development and looking to find a way that could solve it. Overall, I'm having a sf::VertexArray object and looking to reander a shader inside its area. The problem is that the shader takes the window as canvas and only becomes visible in the object range which is not what I'm looking for.. 
      Here's a stackoverflow links that shows the expected behaviour image. Any tips or help is really appreciated. I would have accepted that answer, but currently it does not work with #version 330 ...
    • By noodleBowl
      I just finished up my 1st iteration of my sprite renderer and I'm sort of questioning its performance.
      Currently, I am trying to render 10K worth of 64x64 textured sprites in a 800x600 window. These sprites all using the same texture, vertex shader, and pixel shader. There is basically no state changes. The sprite renderer itself is dynamic using the D3D11_MAP_WRITE_NO_OVERWRITE then D3D11_MAP_WRITE_DISCARD when the vertex buffer is full. The buffer is large enough to hold all 10K sprites and execute them in a single draw call. Cutting the buffer size down to only being able to fit 1000 sprites before a draw call is executed does not seem to matter / improve performance.  When I clock the time it takes to complete the render method for my sprite renderer (the only renderer that is running) I'm getting about 40ms. Aside from trying to adjust the size of the vertex buffer, I have tried using 1x1 texture and making the window smaller (640x480) as quick and dirty check to see if the GPU was the bottleneck, but I still get 40ms with both of those cases. 

      I'm kind of at a loss. What are some of the ways that I could figure out where my bottleneck is?
      I feel like only being able to render 10K sprites is really low, but I'm not sure. I'm not sure if I coded a poor renderer and there is a bottleneck somewhere or I'm being limited by my hardware

      Just some other info:
      Dev PC specs: GPU: Intel HD Graphics 4600 / Nvidia GTX 850M (Nvidia is set to be the preferred GPU in the Nvida control panel. Vsync is set to off) CPU: Intel Core i7-4710HQ @ 2.5GHz Renderer:
      //The renderer has a working depth buffer //Sprites have matrices that are precomputed. These pretransformed vertices are placed into the buffer Matrix4 model = sprite->getModelMatrix(); verts[0].position = model * verts[0].position; verts[1].position = model * verts[1].position; verts[2].position = model * verts[2].position; verts[3].position = model * verts[3].position; verts[4].position = model * verts[4].position; verts[5].position = model * verts[5].position; //Vertex buffer is flaged for dynamic use vertexBuffer = BufferModule::createVertexBuffer(D3D11_USAGE_DYNAMIC, D3D11_CPU_ACCESS_WRITE, sizeof(SpriteVertex) * MAX_VERTEX_COUNT_FOR_BUFFER); //The vertex buffer is mapped to when adding a sprite to the buffer //vertexBufferMapType could be D3D11_MAP_WRITE_NO_OVERWRITE or D3D11_MAP_WRITE_DISCARD depending on the data already in the vertex buffer D3D11_MAPPED_SUBRESOURCE resource = vertexBuffer->map(vertexBufferMapType); memcpy(((SpriteVertex*)resource.pData) + vertexCountInBuffer, verts, BYTES_PER_SPRITE); vertexBuffer->unmap(); //The constant buffer used for the MVP matrix is updated once per draw call D3D11_MAPPED_SUBRESOURCE resource = mvpConstBuffer->map(D3D11_MAP_WRITE_DISCARD); memcpy(resource.pData, projectionMatrix.getData(), sizeof(Matrix4)); mvpConstBuffer->unmap(); Vertex / Pixel Shader:
      cbuffer mvpBuffer : register(b0) { matrix mvp; } struct VertexInput { float4 position : POSITION; float2 texCoords : TEXCOORD0; float4 color : COLOR; }; struct PixelInput { float4 position : SV_POSITION; float2 texCoords : TEXCOORD0; float4 color : COLOR; }; PixelInput VSMain(VertexInput input) { input.position.w = 1.0f; PixelInput output; output.position = mul(mvp, input.position); output.texCoords = input.texCoords; output.color = input.color; return output; } Texture2D shaderTexture; SamplerState samplerType; float4 PSMain(PixelInput input) : SV_TARGET { float4 textureColor = shaderTexture.Sample(samplerType, input.texCoords); return textureColor; }  
      If anymore info is needed feel free to ask, I would really like to know how I can improve this assuming I'm not hardware limited
    • By John Mckrugins
      My short-term  goal right now is a job as a Junior Programmer in any game company, just to get my foot int the door and start earning some income.
      My long term goal is to Programme for bigger more established  game companies and help games that interest me.
      Im in semi-fortunate position where i don't have to work a full time job so i have the  learn how to programme.
      i did my research into whats a good beginner way to start,  Unity and C# came up a lot, so i threw my hat in.
      For the past 5 months i've been learning C# and Unity using the udemy tutorials at a slow but steady pace as i come from a 0 maths/ programming background.
      Right now  getting the hang of things , understanding most code and the  unity engine to a point where i feel comfortable at my current level around Beginner/ Intermediate.
      Although im still keen to continue with Unity, I cant help this nagging feeling that(lets say for arguments sake i do actually get a job) if i do peruse this path and end up with a job as a developer for a company that uses Unity or whatever else uses C# . There is going to be a point at however many X years down the line i am, im still using unity,  im going to be in a position where i want to work on bigger more mainstream games that use C++.
      I want to get a job ASAP, i know it will take at the very least another 7 months probably, learning more code, making a portfolio and all the rest, so i dont really want to change and start from scratch again.
      Im not bashing unity but it looks like its main use is mobile games, which would be perfectly fine as a starting point, but not as a long term career. 
      Hypothetically  If i continue to focus on learning C# / Unity to reach my goal, and at some-point i want to move into bigger prospects and learn C++, how screwed would i be if i wanted to transition over.
      Im by no means a  smart guy that picks up things fast, im just willing to put in the time/effort.
      Should i scrap learning C# and unity for C++ / Unreal  or just power on through and transition later down the line after i get some experience first.
      Time is a factor, but i want to make sure im not condemning myself to a path i wont like later down the line...
       
       
  • Popular Now