Jump to content

  • Log In with Google      Sign In   
  • Create Account

Juliean

Member Since 29 Jun 2010
Offline Last Active Today, 03:25 PM

#5292731 My C++ gotcha of the day

Posted by Juliean on 21 May 2016 - 12:29 AM

Huh? i dont see how the macro is related to this in this case, I generally do tend to avoid them but in this case it is just a shorthand for "extern ACCLIMATE_API template class XXX", and I do know what it stands for so whether seeing the macro or the written-out version doesnt seem to make a big difference to me.


#5292595 My C++ gotcha of the day

Posted by Juliean on 20 May 2016 - 03:28 AM

Huh, when its unclear where the issue is coming from, thats the worst :/

Had something along those lines for some time now. So in my runtime-type system I had a SFINEA-expression to check if a registered object has a reference-counter, or not:

template<typename ObjectType>
static auto AddRef(ObjectType* pObject, int) -> decltype(pObject->AddRef(), void())
{
    if(pObject)
        pObject->AddRef();
}

template<typename ObjectType>
static void AddRef(ObjectType* pObject, long)
{
}

For certain types under certain conditions, this simply was not working. Even though the object had a reference-counter, AddRef was called with the empty implementation, in certain parts of the code. When debugging explicitely, this behaviour somehow depended on which files where included, whether I forward-declared the type in question or not, and so on. There was no clear pattern to it, and I thought it was a compiler bug for the longest time.

 

So than the other day, I noticed that at a certain point I was doing this:

template<typename Type>
class Asset; // this is forward declared in another header

class MyObject
{
	// this is a fully declared class
};

 // exports dll-symbols for the type system for MyObject and Asset<MyObject>
EXPORT_ASSET_OBJECT(MyObject);

At this point, I was generating the export-symbols for the refCounter<Asset<MyObject>>, one of the types that were making difficulties. Turns out that because "Asset" is only forward declared here, it doesn't have an "AddRef"-method as far as the compiler can see it, so the empty SFINEA-expression is generated -.-

This explained why it depended on whether I included specific files, and/or was using the DLL symbol in the test project or not, and so on.

Thats really weird, I expected for SFINEA to eigther give some compilation error that the class was not defined at this point, but not just silently tread it as if it was an empty class the whole time.

 

 

 

 




#5291680 Templated Function results in Circular Inclusion

Posted by Juliean on 15 May 2016 - 04:16 AM

So since I also got it to compile the way you posted, I assume the following:

 

- Component and IApp are actually in different header files

- Your code at some place usinng the Component::registerEvent-function only includes "Component.h", without including "IApp.h"?

 

If this is true, you can do it this way: Since apparently you cannot have circular-inclusions of inheriting classes, you need to create a non-templated function in your Component-class, and define it in the cpp. There, you can include "IApp.h", and that is where you call its functions.

// Component.h

#pragma once
#include "IApp.h"
class IApp;

class Component
{
	IApp* app;

	// Error: Invalid use of incomplete type 'class IApp'
	template<typename T>
	void registerEvent(const int& evtId, int(T::*func) (int))
	{
		auto res = std::bind(func, (T*)this, std::placeholders::_1);
		registerAppEvent(eventId);
	}

	void registerAppEvent(int eventId);
};

// Component.cpp
#include "Component.h"
#include "IApp.h"

Component::registerAppEvent(int eventId);
{
    app->registerForEvent(eventId);
}

If my preassumption was wrong, this might not present a solution however.

 

On a side-node, any reason why you pass evtId as const int&? In case of small primitive types, you should always pass them by value instead of const-reference unless totally require otherwise, since there is absolutely no speed-gain by doing so.




#5291442 VS2013 Annoyance - Linking after compile errors

Posted by Juliean on 13 May 2016 - 12:26 PM

For the life of me I can't remember if this has always been the case, but recently I've noticed that in a large project, I can trigger a compile error in some arbitrary translation unit, and the build process will happily go on to attempt to link the executable anyways.

I swear this used to be a hard build failure and that linking would only be attempted if compilation is clean. I'm too lazy to reinstall some ancient VS version and check, though :-P

 

 

This also happens to me too, although I'm using VS2015. Began happening after I installed Update 2, maybe they also released an update for 2013 that somehow contained the same errors too?

 

I belive this has something to do with partial recompilation being broken/bugged (the thingy using the new IOBJ/IPDB-thingy, does VS2013 even have that?), since I also get different, but similar errors:

 

- After changing the definition of some method, say from "void foo(int)" to "void foo(float)" I get an "unresolved symbol "void foo(int)" error in some modules that use this function (from projects using the DLL of the project that exports this function). This sometimes goes away if I make another change to the function, sometimes only with recompile.

 

- Broken symbols in projects that use this DLL after modifying the API, eigther "this cpp differs from the one compiled", or sometimes straightfoward "no symbols were loaded".

 

- Straightfoward crashes in depending projects after making some changes at random, nonsensical places - I quadripple checked those, they were merely related to a compiler bug and went way after a recompile.

 

- After removing some class members, natvis/intellisense sometimes still points to the old "version" of the class and shows garabage data, while the programm will run happily.

 

Seems like I got a lot more different problems than you, but I belive they all fit in the same scheme. Those are mostly resolved by a complete recompile. Happens on both my PC and my laptop, both after I installed the latest update.

Good to see someone else is having this too (or rather sad), also started to belive I am going insane at some point...

Its also goddamn annoying, having to recompile like 10 times a day...




#5290081 how good is rand() ?

Posted by Juliean on 04 May 2016 - 10:26 AM

Not all that good.

If you have access to C++11 and the STL, try the "<random>" header.

#include <random>

void main(void)
{
    // the random-device/generator, there is a few different of those
    // you create one at the start of your application, or multiple if you want to seperate RNG of different components
    std::mt19937 randgen((unsigned int)time(nullptr));
    
    // the distribution, there are different types for float, int, ...
    // you can create this on-the-fly inside a function
    const std::uniform_int_distribution<int> distribution(0, 1024);
    
    const auto randomNumber = distribution(randgen);
}

You are supposed to get way better results with that.




#5289432 Optimization issue when using operator=

Posted by Juliean on 30 April 2016 - 10:30 AM

I make a simple assignment using my Vec2 operator= for 10m iterations. It takes roughly around 390ms compare to glm's vec2 that is 28ms on my machine. I even removed the code inside it, it is still like that. This is completely the opposite when I'm doing a multiplication (operator*), which is ~410ms vs ~668ms.

 

What kind of optimizations are you applying in your compiler? I assume you are testing with "debug" in Visual Studio (or any other equivalent), since otherwise I strongly assume the loop would be optimized out anyways since it doesn't do any work. What that means is that first of all you are not using the the write-to vector anywhere outside of the loop, which is case one for a compiler to optimize it away, and case two is that you are assigning the same value over and over which could also be eliminated.

 

Why is this important? Any benchmark in debug mode is pretty pointless. Enable optimizations/switch to "release" first, which will most likely result in the loop taking 0 ms at first due to optimizations (which can be fixed by doing "std::cout << vres" after the loop; unless the compiler chooses to optimize the loop based on the second case, for which I do not have a solution).

 

A loop with an empty assignment-operator, if executed with proper optimization should take like not even 1 ms. Then you can see what the real difference between yours and glms class is.

As for why it could be slower: GLM uses template types, which are more likely to be inlined IIRC, which can be a huge deal if you are running in an unoptimized build. See how it behaves in release first, and then if it still doesn't change try "force_inlining" the function.




#5289130 Returning by value is inevitable?

Posted by Juliean on 28 April 2016 - 01:37 PM

First of, and kind of offtopic: Where the hell is the selective quoting function?? Using the regular "quote" button is a pain in the ...
 

Furthermore, __vectorcall and some related optimizations have benefits even for non-SIMD code. Turning on the right set of optimizations and inspecting assembly, you might notice that all kinds of little structs get passed in registers. For ABI reasons, older calling conventions aren't allowed to pass small structs in registers. With /ARCH:AVX and /Gv for instance I find that most of my data gets passed in SSE registers, even non-SIMD things.

 

Oh, my bad, didn't even realize vectorcall was designed primarily for SIMD-code. I didn't come around improving my math-library to use SIMD-code, but after your reply and reading more into it, I'll try out vectorcall and fastcall and if it improves anything in my codebase.

 

Homegrown benchmarks are almost always meaningless, unfortunately. It's exceedingly difficult to be sure that you're benching what you think that you're benching.

 

Well, thats true, I'm not very experienced with running meaningful benchmarks eigther, so might as well be meaningless. It still convinced me to pass most of my small structs by value, and I didn't receive any large performance hit from it... well not that I could see, benchmarking this from an entire application seems to be really hard to me.




#5288766 Returning by value is inevitable?

Posted by Juliean on 26 April 2016 - 10:13 AM

I want to calculate the cross product of two vectors in a class which will be stored in temp variable. When I call on this method I cannot return it by const ref because the stack will be destroyed when the method returns I dont want to return by value because there is a lot copying if I use this method frequently.How can I make a compromise between efficiency and correctness ?
thanks

 

You don't even need to. Any halfway modern compiler will perform RVO (https://en.wikipedia.org/wiki/Return_value_optimization), which means that

Vector cross(const Vector& vector, const Vector& vector2)
{
    Vector crossProduct;

    return crossPoduct;
}

const auto outCrossProduct = cross(v1, v2);

is equivalent to:

// this is somewhat along the lines of what the compiler will do with the entire code above

void cross(Vector& crossProduct, const Vector& vector, const Vector& vector2)
{
}

Vector outCrossProduct;
cross(outCrossProduct, v1, v2);

The compiler will construct the otherwise internal "crossProduct" variable directly in the "outCrossProduct"-variable, with no additional copy-constructors/destructors called. If you are really unsure about it, you can eigther check assembly or test this behaviour with putting cout-statements in your copy-ctor/dtor (those won't even be called because RVO might alter side-effects). Just make sure to turn on optimizations.

 

EDIT: Oh, forgot to mention, the first version of the function with RVO can probably perfom even better than if you just wrote the second one, due to aliasing. See how you have to pass in 3 references to "Vector"? The compiler has to assume all of the can point to the same memory location and thus has to produce safer, but slower code. You cannot use "restrict" here though, because taking the cross product of the same vector with itself might be a valid operation. So the second version at least can make optimizations regarding that the local "crossProduct" cannot alias with "vector" and "vector2", while in the second example it can, most likely resulting in better code generated for the first version than the second.




#5287157 [VS2015] Natvis: inspect vector data in custom visualiser

Posted by Juliean on 16 April 2016 - 02:34 AM

Your getting into implementation details of std::vector by using _Myfirst. That kind of code isn't portable. Why can't you just call m_vComponents.data()? as in:

<Item Name="Components">m_vComponents.data()</Item>

Of course I'm not familiar with how natvis works, and there are peculiarities related to it.

 

I'd love too and trust me, this would make some other natvis-code much easier, but unfortunately you cannot call any kind of function in natvis (similar to breakpoint-conditions & debug-watch) because of possible side-effects (tried it, gives an error message).

Portability should be only half-bad though, since natvis is only used by VS2013+ anyways, and I make a conditional display based on some version-define anyways if I want to. Problem is just that it cannot be done that way :(




#5287069 [VS2015] Natvis: inspect vector data in custom visualiser

Posted by Juliean on 15 April 2016 - 12:32 PM

Thanks for the suggestion,
I know I can do that, but the problem is that m_vComponents is of type char, and I want to view it as "ComponentType"-template type.
I think a hackfix could be to store an additional pointer variable of type ComponentType* and assign m_vComponents.data() to is, but I'd really prefer not to make modifications to my code in order to do this.


#5282038 (split thread) c-style casts

Posted by Juliean on 19 March 2016 - 01:18 PM


static_cast<T*> == (T)
reinterpret_cast<T*> == (T*)
const_cast<T*> == (const T*)

I've not once run into a bug caused by C style casting that would not also have been caused by one of those C++ casts. So instead of just shooting yourself in the foot, you should get three guns to choose from?

Just a minor complaint about your usage of const cast - const_cast<const T*> is wrong, the way you use it is actually const_cast<T*>, and thus the equivalent c-style cast is (T*) - thats one of the reasons why C-style casts can be considered bad, because you might just cast a const modifier away where you didn't expect to.




#5281120 how could unlimited speed and ram simplify game code?

Posted by Juliean on 13 March 2016 - 04:10 PM

Some points I thought of:

 

- Physics would be totally simplified. No more broad/narrowphase, bounding volumes, etc... just good old per-vertex n^2 collision checks (well you would probably still want to use bounding volumes for stuff like characters, but you don't have to anymore!)

 

- Pretty much any form of preprocessing could be gone. BSP-Trees, static light/shadow maps, you name it. This could heavily simplify code, especially on the tool side of things. Moved an object, need to notify the build pipeline and rebuild the static shadowmaps? Nope, not anymore, just take everything as it is and use it

 

- Any form of manual optimization would be gone. Memory layout, SSE, Multithreading (for the most part), Assembler... I can't speak for everyone but I for sure would prefer not having to write Struct-of-Array style code, for the most parts.

 

- Oh, and lets not forget about all the GPU-centered algorithms. GPU-Particles, GPU-Clothing, GPU-Watersimulation. Would be way easier if we could just write all those algorithms on one processor, I personally find regular CPU-code easier to work with than ie. compute-shader based equivalents.

 


all object types could have all possible member variables for all object types (unlimited ram), so you only need one type of "uber" game object type for everything.

 

Uh, why would you want to do that now? The reason we don't have "uber"/god classes is not because RAM doesn't support it, but because its harder to read through on 100k line cpp file, than it is through 1000 100 line ones (since you are only interested in part of the code anyways). I mean, be it composition/inheritance or whatever, but a door is a door, and not an "if(i_am_a_door) doDoorStuff();" alongside another thousands of those in a huge god class, for every possible game object, unless I misinterpreted your intention here.




#5276038 Template Specialization

Posted by Juliean on 16 February 2016 - 05:16 PM


Helper class works correctly, but I'm sitting here staring at it wondering what the hell I'm looking at. Could you explain this to me?

 

Sure do.

 

So first of our outer class should act as a replacement of std::is_aritmetic, so we set it up the interface like this:

template<typename T>
class acceptsType
{
public:
    static const bool value = //???
};

So the value of the compile-time evaluated "value" variable should depend both on the type, as well as the value of is_aritmetic. So we do just that, by defining an inner helper class that takes two template arguments:

template<typename T2, bool isAritmetic = false>
class helper
{
public:

    static const bool value = false;
};

So now we call it by making it take <T, std::is_aritmetic<T>::value>, and assigning its "value" to the outer-class "value":

static const bool value = typename helper<T, std::is_arithmetic<T>::value>::value;

And the base template "helper" class now defines that for any type where is_arithmetic is "false", "value" will also be false. But we want to support some types though, so we add a specialisation for every arbitrary type where is_arithmetic is actually true:

template<typename T2>
class helper<T2, true>
{
public:

    static const bool value = true;
};

This uses partial specialisation, and translates to what I've just said - arbitary T2, and std::is_arthmetic == true will generate this overload.

 

So at this state adding anything except int, float, ... will return "false" and thus fail your SFINEA. But adding additional types is now really easy.

template<>
class helper<std::string, false>
{
public:
    static const bool value = true;
};

This time, we use a full specialistation - we know the value of T2 (std::string), and that it is not arithmetic. This combats the ambiquity that you described in your attempts to fix it - due to the second template parameter, you always get a closes match for the types.

 

 

 

Hope this solves any questions, if you are still confused, I'll try to explain anything that is left over.




#5275954 Template Specialization

Posted by Juliean on 16 February 2016 - 09:43 AM


In the existing function it prevents compilation if the type is not arithmetic. The function body handles arithmetic types, but I also want to start supporting specific non-arithmetic types using the same signature but different function bodies. (Beginning with std::string, yes.)



This is in VS, so the way I have it now does the red underline at the call site when a non-supported type is entered, which is sort of what I'm after.



How would you go about using static_assert for this?

 

Okay, so at least I was right about the purpose.

 


Should work in my opinion but I don't have a relevant compiler close enough to check.

 

Unforunately what this doesn't do is add the red underline in VS, at least from what I tested with VS2015.

 

So in case you are the only one adding specialisations to this function, you can use my first suggestion, otherwise BitMasters' (if it fixes the problem).

 

Aside from that, you'll have to think about what you prefer: Red underlines wherever someone uses an imcompatible type with this function, probably followed by a cryptic template-error-message (using SFINEA), or no underlining but a meaningful error message that you yourself can provide for the user (using static_assert). Probably depends on your use-case, which of the two is better.




#5275923 Template Specialization

Posted by Juliean on 16 February 2016 - 08:56 AM


std::enable_if is already C++11. You might be thinking of the old boost::enable_if. That said, I'm not sure about the exact semantics of static_assert but can it really be used to trigger SFINAE, because that's usually the whole point of enable_if.

 

Ah right, wasn't sure if enable_if is already in C++11 or not.

 

No, static_assert won't trigger SFINAE, but that was kind of my point - as with the code example given, I don't see much points in him using SFINEA. What he shows and describes is a function that should fail to instantiate if a incompatible type is used - which is what static_assert does, but only with a better error output. I posted both my solution and the comment towards static_assert based on whether he really wants to use SFINEA here, or not :)






PARTNERS