Long time no see and Input Capture

The Partridge Family were neither partridges nor a family. Discuss.
albinopapa
Posts: 4373
Joined: February 28th, 2013, 3:23 am
Location: Oklahoma, United States

Re: Long time no see and Input Capture

Post by albinopapa » November 14th, 2018, 4:10 am

I kind of like this discussion on threading and exceptions: https://stackoverflow.com/questions/233 ... en-threads.

One of the answers was to have try/catch blocks in each thread and have them deal with handling their own stuff, then set a shared flag telling the other threads there was an error and they need to stop what they're doing, clean up and abort.

Another suggestion was to use std::exception_ptr, this way you can have the other threads check this pointer and if it's a valid object, clean up and abort, the main thread can handle as needed.

You'd just have to play around with it I guess.
If you think paging some data from disk into RAM is slow, try paging it into a simian cerebrum over a pair of optical nerves. - gameprogrammingpatterns.com

albinopapa
Posts: 4373
Joined: February 28th, 2013, 3:23 am
Location: Oklahoma, United States

Re: Long time no see and Input Capture

Post by albinopapa » November 14th, 2018, 4:18 am

That conditionalcall is fancy as well; tho I'm not sure it saves any code.
Yeah, that was just my first thought to avoid specifically having the AddDelay forwarding function in the Input class. Plus, I made it before looking through the rest of the code to determine if any of the other data structures needed their own conditionally called function.

So, while it may not save any code up front, it's a compile time choice between the two lambdas instead of using the runtime if( get_if<>() ) case, so it might save a runtime branching operation or two somewhere down the road.

With your templated function function pointers, you could probably get away with using something similar instead, though I don't the circumstances of their use.
If you think paging some data from disk into RAM is slow, try paging it into a simian cerebrum over a pair of optical nerves. - gameprogrammingpatterns.com

albinopapa
Posts: 4373
Joined: February 28th, 2013, 3:23 am
Location: Oklahoma, United States

Re: Long time no see and Input Capture

Post by albinopapa » November 14th, 2018, 4:24 am

That is such a coincidence though, I've spent the last 4-5 weeks messing around with std::variant and have been wanting to use it for something, and here you go using it. Actually, the base interface idea is something that I just recently came up with ( though I'm not the first ) and am glad your project came along to give me a chance to experiment.

I attempted an ECS using std::variant, but failed due to std::variant needing knowledge of each of it's times during compilation, so I was running into circular dependencies.
I am currently attempting to work something out using the Box2D API, but I'm afraid it would have to be entirely rewritten to work in something like that.
If you think paging some data from disk into RAM is slow, try paging it into a simian cerebrum over a pair of optical nerves. - gameprogrammingpatterns.com

User avatar
chili
Site Admin
Posts: 3948
Joined: December 31st, 2011, 4:53 pm
Location: Japan
Contact:

Re: Long time no see and Input Capture

Post by chili » November 15th, 2018, 6:34 am

Exception marshalling between threads can be a pain in the dick. Another reason why std::async (future) is so goddamn sexy.
Chili

albinopapa
Posts: 4373
Joined: February 28th, 2013, 3:23 am
Location: Oklahoma, United States

Re: Long time no see and Input Capture

Post by albinopapa » November 15th, 2018, 10:37 pm

Out of all the C++ features or library APIs, std::variant is one of the most fickle.

Because everything needs to be known at compile time, there's a huge chance that there is going to be circular dependencies. One thing that I've noticed between using a visitor struct with overloaded call operators and using the lambda visitor to get the underlying object is the latter give more exact information if anything goes wrong.

As an example, Box2D has 4 shapes it deals with for collision; circle, polygon, edge, chain. And creates contacts between a few of these shapes; circle/circle, circle/polygon, circle/edge, edge/polygon, polygon/polygon, chain/circle and chain/polygon. Ok, so if I want to call a collision function with two of these shapes, I think it would be best to use a struct, let's call it b2Collider where I have overloaded operator() for each of the above pairs as well as mirrors of them and lastly a templated (typename T, typename U) catch for unsupported pairs. Chains and edges don't collide and they don't collide with themselves. So when I call the function to initiate collision handling, I have something like: std::visit( b2Collider( ... ), shape1, shape2 ). Now, if everything is setup correctly, this is how you visit two variants, but somethings wrong and I get: There is no overload of std::visit blah,blah,blah.

Now, before I went the route of a b2Collider struct, each supported pair had a specialized b2Contact child, which had an Evaluate function. For instance: b2ChainCircleContact, which was derived from b2Contact. In the Evaluate function, it would cast the b2Shape* to it's derived type and pass it along to the function that would handle the collision, etc... Well, my first attempt was just going to be the pseudo inheritance pattern I am pushing off to you, and I got the error message: b2ChainCircleContact has no Evaluate() member, which is a lot more descriptive and informative.

The reason for the change then, was because the class did in fact have an Evaluate function, but for some reason it wasn't being recognized. Now, for some reason when I try to compile, I get an error saying namespace std has no members for optional and vector. Both headers are included at the site of use, both template parameters are from headers that are included in same header they are used in, so the only thing that I can think of is circular dependency somewhere, but I am having troubles finding it.

One of the biggest issues is how intertwined everything is. There are a lot of forward declarations, a lot of shared responsibilities between different classes and other things. So I don't see how std::variant could be incorporated into a large project.
If you think paging some data from disk into RAM is slow, try paging it into a simian cerebrum over a pair of optical nerves. - gameprogrammingpatterns.com

cameron
Posts: 794
Joined: June 26th, 2012, 5:38 pm
Location: USA

Re: Long time no see and Input Capture

Post by cameron » November 16th, 2018, 2:58 am

Yeah I would tend to agree papa. std::variant after all is most like a union and a union also has the same requirements of needing to know all the types up front. So where a union wouldn't work a variant also would not. It is nice how you can have static polymorphism through std::visit with variants however and variants overall have more functionality. In specific instances, I can see their use. In this case, I was just fooling around and may have been better to go with virtual functions and polymorphism. Idk though, with your input class it seems about equal. I think ill probably end up using a slightly modifed version of your input class. I don't see a reason to move (back) over to polymorphism now that its already done.

Yeah... I could probably get exceptions working with this project if I wanted to but it seems too much of a hassle. I've never been a huge fan of exceptions either. Wonder how exceptions would work with std::async though.

As far as the key combo issues, the best I can figure out is windows doesnt let certain key combos reach the message queue (even with raw input). Not sure how to get around this though. Im sure a custom device driver would work but im not doin that... Ive read a bit about keyboard hooks but im not sure that would work or suit my needs.
Computer too slow? Consider running a VM on your toaster.

albinopapa
Posts: 4373
Joined: February 28th, 2013, 3:23 am
Location: Oklahoma, United States

Re: Long time no see and Input Capture

Post by albinopapa » November 16th, 2018, 5:34 am

As far as exceptions go, I usually prefer them just because they allow me to focus more on return by value, so I can have const objects instead of

Code: Select all

MyObject object;
if( !object.Initialize( params )  )
{
    // return false;
    // or MessageBox( nullptr,  _T("Failed to initialize MyObject", _T( "Error" ), MB_OK );
}

// I can just do 
const MyObject object( params );
I get the efficiency of using the constructor and the ability of making it const, which also might have efficiency gains. The constructor would throw maybe a std::runtime_error("Failed to initialize MyObject" ); exception and can be caught in the WinMain function instead of having error handling code littered all over the place.

Look at the DX and WinAPIs, they return an HRESULT and use in/out params, where it would be cleaner to just return a damned object or pointer to some interface, and throw if something went wrong. Unfortunately, because they use the same code for multiple languages, returning error codes and using pointer to pointer in/out parameters saves them on development time.

Well, maybe not dev time, as the implementations are different between Java/C/C++/C#/etc..., but the interfaces should be relatively the same.
If you think paging some data from disk into RAM is slow, try paging it into a simian cerebrum over a pair of optical nerves. - gameprogrammingpatterns.com

albinopapa
Posts: 4373
Joined: February 28th, 2013, 3:23 am
Location: Oklahoma, United States

Re: Long time no see and Input Capture

Post by albinopapa » November 16th, 2018, 5:46 am

As to polymorphism, I have been trying to get out of using it as much as possible, choosing templates or more recently std::variant to avoid individual memory allocations of derived types. It seems you can actually go a long way with something like CRTP ( curiously recursive template pattern ) where the template is a base class templated on the derived class

Code: Select all

template<typename Derived>
class Base
{
public:
     void ReadData( std::ifstream& _stream )
     {
          Derived*const self = static_cast<Derived*const>(this);
          self->ReadData( _stream );
     }
};

class Derived : public Base<Derived>
{
public:
     void ReadData( std::ifstream& _stream )
     {
          // deserialize your data here
     }
private:
     // your data
};
I'm not a pro with this pattern though, so I may have gotten something wrong, and am not entirely sure how much more or less useful it is over std::variant, and since templates define a completely unique type based on template parameters, I don't think this can go into a vector unless you also use variant.
If you think paging some data from disk into RAM is slow, try paging it into a simian cerebrum over a pair of optical nerves. - gameprogrammingpatterns.com

cameron
Posts: 794
Joined: June 26th, 2012, 5:38 pm
Location: USA

Re: Long time no see and Input Capture

Post by cameron » November 20th, 2018, 9:35 pm

Just curious about your reasoning for avoiding polymorphism.

Also I cannot seem to get this template code to compile (fooling around with my function classes)

Code: Select all

enum CallingConvention { CDECLR, STDCALL, FASTCALL, THISCALL };
template<CallingConvention> struct CCHelper;

template<> struct CCHelper<CDECLR>
{
	template<typename RT, typename... Args>
	using PFunc = RT(__cdecl *)(Args...);

	template<typename RT, typename O, typename... Args>
	using PFuncM = RT(__cdecl O::*)(Args...);

	template<typename RT, typename O, typename... Args>
	using PFuncMC = RT(__cdecl O::*)(Args...) const;
};
template<> struct CCHelper<STDCALL>
{
	template<typename RT, typename... Args>
	using PFunc = RT(__stdcall *)(Args...);

	template<typename RT, typename O, typename... Args>
	using PFuncM = RT(__stdcall O::*)(Args...);

	template<typename RT, typename O, typename... Args>
	using PFuncMC = RT(__stdcall O::*)(Args...) const;
};
template<> struct CCHelper<FASTCALL>
{
	template<typename RT, typename... Args>
	using PFunc = RT(__fastcall *)(Args...);

	template<typename RT, typename O, typename... Args>
	using PFuncM = RT(__fastcall O::*)(Args...);

	template<typename RT, typename O, typename... Args>
	using PFuncMC = RT(__fastcall O::*)(Args...) const;
};
template<> struct CCHelper<THISCALL>
{
	template<typename RT, typename... Args>
	using PFunc = RT(__thiscall *)(Args...);

	template<typename RT, typename O, typename... Args>
	using PFuncM = RT(__thiscall O::*)(Args...);

	template<typename RT, typename O, typename... Args>
	using PFuncMC = RT(__thiscall O::*)(Args...) const;
};
The following wont compile:

Code: Select all

template<CallingConvention CC, typename RT, typename... Args>
using PFunc = typename CCHelper<CC>::PFunc<RT, Args...>;

template<CallingConvention CC, typename RT, typename O, typename... Args>
using PFuncM = typename CCHelper<CC>::PFuncM<RT, O, Args...>;

template<CallingConvention CC, typename RT, typename O, typename... Args>
using PFuncMC = typename CCHelper<CC>::PFuncMC<RT, O, Args...>;
-edit:
Im thinking there is an easier way of doing this (by this I mean accepting any kind of function pointer (not std::function).
Computer too slow? Consider running a VM on your toaster.

albinopapa
Posts: 4373
Joined: February 28th, 2013, 3:23 am
Location: Oklahoma, United States

Re: Long time no see and Input Capture

Post by albinopapa » November 20th, 2018, 11:16 pm

Well, templates can allow you to accept any callable

Code: Select all

template<typename RType, typename Fn, typename...Args>
auto VoidReturnFn( Fn&& _callable, Args&&... _args)->RType
{
    if constexpr( std::is_void_t<RType> )
        _callable( std::forward<Args>(_args)... );
    else
        return _callable( std::forward<Args>(_args)...);
}
You'd probably have to make alternates for const and noexcept variants.

This would be how to accept any callable from lambdas to functor objects to function pointers.

As far as storing a vector of callables, you might be stuck with std::function.
If you think paging some data from disk into RAM is slow, try paging it into a simian cerebrum over a pair of optical nerves. - gameprogrammingpatterns.com

Post Reply