Whole Tomato Software Forums
Whole Tomato Software Forums
Main Site | Profile | Register | Active Topics | Members | Search | FAQ
User name:
Password:
Save Password
Forgot your password?

 All Forums
 Visual Assist
 Technical Support
 Problem with auto and templates
 New Topic  Reply to Topic
 Printer Friendly
Author Previous Topic Topic Next Topic  

MauriceG
New Member

Germany
4 Posts

Posted - Oct 25 2019 :  09:32:56 AM  Show Profile  Reply with Quote
I have a big problem when using find References or Goto Implementation in our code. We use a wrapper class for shared_ptr/unique_ptr which I could identify as one of the causes for these problems. I've tried to find a good way to work around those but at some point I can't find a good solution (also I think VA should understand the code without changing it...).

So I wrote a little example:


struct Foo
{
	void Init() {}
};

struct Bar
{
	void Init() {}
};


template<typename Ptr>
class PtrWrapper_Broken final
{
public:
	PtrWrapper_Broken(Ptr p) : m_ptr(std::move(p)) {}

	auto operator->() const noexcept { return &*m_ptr; }
	auto operator*() { return *m_ptr; }

private:
	Ptr m_ptr;
};

template<typename Ptr>
class PtrWrapper_Fixed final
{
public:
	PtrWrapper_Fixed(Ptr p) : m_ptr(std::move(p)) {}

	auto operator->() const noexcept -> decltype(Ptr().operator->()) { return &*m_ptr; }
	auto operator*() { return *m_ptr; }

private:
	Ptr m_ptr;
};

template<typename T>
using UPtrWrapper_Broken = PtrWrapper_Broken<std::unique_ptr<T>>;

template<typename T>
using SPtrWrapper_Broken = PtrWrapper_Broken<std::shared_ptr<T>>;

template<typename T>
using UPtrWrapper_Fixed = PtrWrapper_Fixed<std::unique_ptr<T>>;

template<typename T>
using SPtrWrapper_Fixed = PtrWrapper_Fixed<std::shared_ptr<T>>;

template<typename T>
SPtrWrapper_Fixed<T> make_fixed(T* ptr)
{
	return SPtrWrapper_Fixed<T>(std::shared_ptr<T>(ptr));
}

void test()
{
	std::unique_ptr<Foo> p1(std::make_unique<Foo>());
	p1->Init(); // ok

	std::shared_ptr<Foo> p2(std::make_shared<Foo>());
	p2->Init(); // ok

	UPtrWrapper_Broken<Foo> p3(std::make_unique<Foo>());
	p3->Init(); // not ok, also found for Bar::Init

	SPtrWrapper_Broken<Foo> p4(std::make_shared<Foo>());
	p4->Init(); // not ok, also found for Bar::Init

	UPtrWrapper_Fixed<Foo> p5(std::make_unique<Foo>());
	p5->Init(); // ok

	SPtrWrapper_Fixed<Foo> p6(std::make_shared<Foo>());
	p6->Init(); // ok

	auto p7 = p6;
	p7->Init(); // ok

	auto p8(std::move(p5));
	p8->Init(); // not ok, missing in Find References for Foo::Init

	auto p9(std::move(p1));
	p9->Init(); // not ok, missing in Find References for Foo::Init

	auto p10 = p2;
	p10->Init(); // ok

	auto p11 = make_fixed(new Foo());
	p11->Init(); // not ok, missing in Find References for Foo::Init

	SPtrWrapper_Fixed<Foo> p12 = make_fixed(new Foo());
	p12->Init(); // ok
}


In this example I create a smart pointer for Foo. What I want is that Goto Implementation jumps directly to Foo::Init and Find References on Foo::Init finds the calls in the main function while Find References on Bar::Init finds only the implementation itself. On all lines with "not ok" it gives me a huge list of Init methods for Goto Implementation (most of them from the Windows SDK).
Find References on Bar::Init lists me two lines from the test function while Find References on Foo::Init misses 3 of the calls in test.

PtrWrapper_Fixed was my first attempt to fix it in code, but it only fixes some of the cases. But it should also work without changing the code. Would be great if this can get fixed.

Regards,
Maurice

Edited by - MauriceG on Oct 29 2019 05:27:51 AM

feline
Whole Tomato Software

United Kingdom
17188 Posts

Posted - Oct 25 2019 :  1:20:09 PM  Show Profile  Reply with Quote
Are the problems you are seeing limited to Find References and Alt-G, or are they also effecting listboxes as well? I am seeing the same problems here, but its going to take a bit of digging to figure out why.

zen is the art of being at one with the two'ness
Go to Top of Page

feline
Whole Tomato Software

United Kingdom
17188 Posts

Posted - Oct 28 2019 :  08:47:24 AM  Show Profile  Reply with Quote
OK, I have a partial solution for you to try. First, can you please edit your code to change the two using lines that declare the wrapper templates to be:

#define VA_HELPER_MAKE_UPTRWRAPPER_BROKEN template<typename T> using UPtrWrapper_Broken = PtrWrapper_Broken<std::unique_ptr<T>>;
#define VA_HELPER_MAKE_SPTRWRAPPER_BROKEN template<typename T> using SPtrWrapper_Broken = PtrWrapper_Broken<std::shared_ptr<T>>;

VA_HELPER_MAKE_UPTRWRAPPER_BROKEN
VA_HELPER_MAKE_SPTRWRAPPER_BROKEN


this compiles to exactly the same result, but allows us to tell VA privately to use different definitions for these two macros, and thus bypass VA's parser problem with these wrapper classes.

Remember while the compiler gets to actually instanciate the templates when compiling, VA does not get to do this, so we have to work things out from the code more "directly", which also opens us up to getting confused when you do something clever, which is what is happening here.

Now please press the button:

VA Options -> Performance -> Rebuild symbol databases

and close your IDE.

Before loading the IDE again please go to the base directory of your solution, where your .SLN file lives, and create a new text file called "va_stdafx.h", and give it the following content:

#define VA_HELPER_MAKE_UPTRWRAPPER_BROKEN
#define VA_HELPER_MAKE_SPTRWRAPPER_BROKEN

template<typename PtrHidden>
class UPtrWrapper_Broken
{
public:
	UPtrWrapper_Broken(PtrHidden *ptr);
	PtrHidden *operator->() const;
	PtrHidden &operator*();
};

template<typename PtrHidden>
class SPtrWrapper_Broken
{
public:
	SPtrWrapper_Broken(PtrHidden *ptr);
	PtrHidden *operator->() const;
	PtrHidden &operator*();
};

making sure that the file ends with a blank line.

VA looks for this file deliberately when first scanning your solution, and uses any helper code here to understand your solution. So this takes precidence over the correct definitions of both the macros for the using statements, and the class declarations of the two wrapper classes. So as far as VA is concerned your two wrapper classes are nothing more than simple smart pointer templates, which VA does handle correctly.

You do need to rebuild VA's symbol database when editing this file, but since it is not part of the solution, and is never meant to be part of the solution, it can be checked into your source code control system, so VA will be able to pick it up in future.

Obviously if you need any extra methods in the wrapper classes you can add them to these "helper" wrapper classes in the va_stdafx.h file, and VA will then pick up these methods and use them.

Hopefully all of this makes sense and works correctly for you.

Making an auto with std::move as a parameter is a separate problem that I will think about next.

zen is the art of being at one with the two'ness
Go to Top of Page

feline
Whole Tomato Software

United Kingdom
17188 Posts

Posted - Oct 28 2019 :  11:07:16 AM  Show Profile  Reply with Quote
I think that std::move is related to a known bug:

case=66730

Currently VA is not working out the "calling" template type when a template function is called without specifying the template type, instead just letting the compiler work out the template type from the parameter type.

Due to this bug I cannot use a similar va_stdafx.h workaround to get VA to understand "std::move", and it is probably at the root of why "std::move" isn't working correctly to begin with.

zen is the art of being at one with the two'ness
Go to Top of Page

MauriceG
New Member

Germany
4 Posts

Posted - Oct 28 2019 :  1:20:18 PM  Show Profile  Reply with Quote
I originally didn't test the listbox but it doesn't seem to be affected, which I find strange. It even works with auto, so at some level the knowledge which type is returned by operator->() should be available.

Your workaround seems to work as long as we are using UPtrWrapper_Broken/SPtrWrapper_Broken and not PtrWrapper_Broken<shared_ptr<T>>, which sadly isn't always the case. I should have included examples with that as well.

Go to Top of Page

feline
Whole Tomato Software

United Kingdom
17188 Posts

Posted - Oct 28 2019 :  4:03:51 PM  Show Profile  Reply with Quote
By default, in VS2010 and above, listbox content comes from the IDE first, and then VA second if the IDE cannot produce a listbox. So the listboxes that are working are probably coming from the IDE intellisense parser, which is different to and independent of VA's parser.

I am seeing VA getting confused with the following code:

void felineTestHere()
{
	PtrWrapper_Broken<std::shared_ptr<Foo>> testing1(std::make_unique<Foo>());
	testing1->Init();
}

is this a suitable test case for what you are doing?

zen is the art of being at one with the two'ness
Go to Top of Page

MauriceG
New Member

Germany
4 Posts

Posted - Oct 29 2019 :  05:21:46 AM  Show Profile  Reply with Quote
Yes, that would be one example, but I think a larger part would be as calls on class members:


struct Test
{
	PtrWrapper_Broken<std::shared_ptr<Foo>> m_shared{ nullptr };
	PtrWrapper_Broken<std::unique_ptr<Foo>> m_unique[ nullptr };

	void DoStuff()
	{
		m_shared->Init();
		m_unique->Init();
	}
};

Edited by - MauriceG on Oct 29 2019 05:27:06 AM
Go to Top of Page

feline
Whole Tomato Software

United Kingdom
17188 Posts

Posted - Oct 30 2019 :  3:28:38 PM  Show Profile  Reply with Quote
I think I have a fix for this as well now. The fix is working here, in my simple test cases at least. Can you please edit your "va_stdafx.h" file and add:

template<typename Ptr>
class PtrWrapper_Broken
{
public:
	Ptr * operator->();
private:
	Ptr m_ptr;
};

this will require another rebuild of your VA symbol database to take effect, which you trigger by pressing the button:

VA Options -> Performance -> Rebuild symbol databases

and restarting the IDE.

zen is the art of being at one with the two'ness
Go to Top of Page

feline
Whole Tomato Software

United Kingdom
17188 Posts

Posted - Oct 30 2019 :  3:48:53 PM  Show Profile  Reply with Quote
I think I now properly understand what is going wrong here. VA is not working out the return type when a template class member function is returning "auto". Without "auto" the return type is clear in the function declaration, but here you need to understand the function body as well to work out the return type, and this being a template function complicates things a bit further.

It turns out this is a known bug, so I have added this example to it:

case=87230

zen is the art of being at one with the two'ness
Go to Top of Page

MauriceG
New Member

Germany
4 Posts

Posted - Nov 04 2019 :  11:53:27 AM  Show Profile  Reply with Quote
Thank you for your help, feline. I think with all the changes I did now, I have a version that works a lot better now (except where auto is used).
Go to Top of Page

feline
Whole Tomato Software

United Kingdom
17188 Posts

Posted - Nov 04 2019 :  3:26:48 PM  Show Profile  Reply with Quote
If you have any more specific places where you are having problems I am happy to have a look, to see if we can help VA make more sense of them. But returning auto where it's not going to be clear ahead of time what type we want to treat this as is going to be a problem for now.

zen is the art of being at one with the two'ness
Go to Top of Page
  Previous Topic Topic Next Topic  
 New Topic  Reply to Topic
 Printer Friendly
Jump To:
© 2019 Whole Tomato Software, LLC Go To Top Of Page
Snitz Forums 2000