How to enable completion of C++ template classes in vim using YouCompleteMe - c++

When using the vim plugin YouCompleteMe for C++ code completion I stumbled over an issue.
Using nested template classes stops the completion to work properly.
Consider the following example to reproduce the behaviour:
#include <vector>
template<class T>
class foo {
public:
void Init();
private:
struct bar {
int foobar;
};
bar one_bar;
std::vector<foo<T>::bar> some_bars;
};
template<class T>
void foo<T>::Init(){
one_bar.foobar = 0; // completion as expected
some_bars.at(0).foobar = 0; // no completion neither for "at" nor for "foobar"
}
The code completion for "some_bars" is not working at all while "one_bar" behaves as expected.
How can I get completion working for this code? Is this issue related to the setup and should actually work or is it a bug in YCM?
My system is debian jessie/sid based, vim version 7.4, YCM latest version from GitHub.
Edit:
There are similar issues reported in YCMs bug tracker:
https://github.com/Valloric/YouCompleteMe/issues/243
https://github.com/Valloric/YouCompleteMe/issues/530
Seems to be a bug in clang rather than in YCM. Can someone confirm this?
Edit2:
I opened another issue in the YCM issue tracker.
https://github.com/Valloric/YouCompleteMe/issues/1170
The intention is to get more information on what the bug in clang exactly is and finally to make a bug report in the clang issue tracker.
Edit3:
I followed the proposed procedure from RedX and fed my code in clang to get completions.
Clang does not provide any suggestions for the discussed positions in the code.
This clearly is the reason why YCM fails to make suggestions in vim and it has nothing to do with YCM or vim.
A bug report in the clang issue tracker has been filed:
http://llvm.org/bugs/show_bug.cgi?id=20973

I think, under the rules of C++, you cannot get completion in this case.
Without knowledge of the type T, we don't know what methods std::vector<T> will have, as each instansiation of a template in C++ can have different methods.

As #Chris Jefferson mentioned above it is theoretically impossible .
This comment doesn't take in account template specializations
In the code shown here, all completions are clearly known, even without knowing the type T. It is not a std::vector but a std::vector<foo::bar>
This occurred to me in a very different context while I was trying to write a specialization for a template without deducting a template.I will provide my example to make the situation clear. So lets say you have an enumerator and you have a meta_info class that defines a size for an enumerator it takes by template so :
enum class e{a,b};
template <class Enum>
struct meta_info;
template<>
meta_info<e>{
static constexpr size_t s=2;
}
Ok this works and it is nice to have but what happens if you try to do the same thing in an enumerator nested in a template class?
template <class Tag>
struct str{
enum class e{a,b};
}
template <class Enum>
struct meta_info;
template<T>
meta_info<str<T>::e>{
static constexpr size_t s=2;
}
This doesn't compile as someone else(another part of the code) might change e with another specialization.There is no formal way for a compiler to know if a specialization changes a type even at the first passes of compilation. Having in mind that auto-complete tools most of the times just use the includes paths to find a suggestion this would be impossible. So in the future I would like to see in ycm something like an informal instantiation like VS has done

Related

Can't Pass `std::array` From Within Closure

Using the following declarations:
#include <array>
template <typename V> void makeError(V& v) {}
the following code snippet fails to compile under MSVC 16.11.13 with /std:c++17:
int main() {
[&]() {
std::array bugs{0};
[&]() { makeError(bugs); };
};
return 0;
}
The error message is as follows: main.cpp(9,5): error C2955: 'std::array': use of class template requires template argument list
The same code compiles perfectly fine under GCC 10.4 with --std=c++17. The real problem came up when I tried removing the inner closure:
int main() {
[&]() {
std::array bugs{0};
makeError(bugs);
};
return 0;
}
Or the outer closure:
int main() {
std::array bugs{0};
[&]() { makeError(bugs); };
return 0;
}
Both of these compile happily under MSVC and GCC.
Is the first snippet invalid C++? If so, what part of the standard am I violating?
Looks like this is a bug in msvc's legacy lambda processor. Your code compiles if you pass /Zc:lambda: https://godbolt.org/z/91z4chhTx
This seems to be the matching bug report: https://developercommunity.visualstudio.com/t/class-template-argument-deduction-fails-in-the-bod-1/414204
How I found this
I would always recommend using godbolt in cases like this, since it allows you to quickly switch between compilers, versions and command line args.
If something smells like a compiler bug, checking the latest versions is always a good idea. If something compiles under gcc & clang, but not msvc, it smells like a compiler bug.
I tried your code in the latest msvc and since it involves lambdas and captures, I remembered that there were changes to that in c++20, so I tried compiling as such. Since that worked, I had look through cppreference to see if I could find any relevant change to the standard that might legitimately cause this. I didn't find any, but then remembered that msvc has also constantly be increasing its standards conformance over the recent years.
So I used /permissive- with c++17 and once I noticed that this builds, I had a look through the conformance flags and found /Zc:lambda.
This confirmed this as a fixed compiler bug to me. And fixed bugs often have bug reports, so I googled the keywords that seemed relevant to the situation:
msvc nested lambdas template deduction. The bug report is the second result for me.

Specialised template *not* asserting despite compile time constant static_assert

For a few days I've been using and testing my application without any trouble using the following code:
class dataHandler
{
public:
template<class T>
T GetData(bool truncate = false) { static_assert(false, "Unhandled output type"); }
template<T>
int GetData<int>(bool truncate)
{
// Normal stuff that compiles
}
}
This (As I expected at the time) works fine as long as no implicit instantiation of GetData's default is performed. However, today after adding a new failing specialisation for void* specifically (I wanted a different error) I discovered it wouldn't compile due to the assertion, even though the `void* spec was never called or mentioned in code.
I started a new C++ test project (with the same C++ version, MSVC version, and VS2022 version) and found this also doesn't compile for the same reason:
template<class T>
T Spec()
{
static_assert(false, "default");
}
template<>
int Spec<int>()
{
return 1;
}
I was unable to replicate the original 'success' in anything I tried within the test project, and the failing GetData<void*> assert in the initial project seems to indicate that it's not a project config / difference in toolset issue.
After some searching I discovered that it failing was the intended (or otherwise expected) behaviour, as well as a workaround for it.
However I find myself wondering why the initial case of static_assert(false) didn't also fail to compile. Is this some niche exception, or is this an issue with MSVC's consistency?
However I find myself wondering why the initial case of static_assert(false) didn't also fail to compile. Is this some niche exception, or is this an issue with MSVC's consistency?
It's certainly not an inconsistency. The key part here is "Standard conformance mode", controllable with the compiler option /permissive-. See the documentation of this compiler option.
The reason that MSVC from before VS2017 used to accept your static_assert(false, ...), is because it postponed parsing the contents of a function template until template instantiation. That is just how the parser used to work, and also why features like two-phase name lookup could never be properly implemented. I'll refer you to this blog post for more background on this.
You can easily try it for yourself. Even if the function contains ill-formed code that shouldn't even parse correctly, the compiler doesn't complain as long as the template isn't being instantiated:
template<class T>
void foo()
{
Normally this should not (never actually (compile)) but it does anyway
}
It appears to do some basic paranthesis matching and that is it. The token stream is recorded and parsed when foo is instantiated (usually when called).
In order to fix two-phase lookup (and other conformance issues), they had to fix the compiler and parse the code whenever a function template is encountered, rather than being instantiated. But now they have a problem, because a lot of old code might be reliant on the old compiler behavior that suddenly doesn't compile anymore. So they introduced the /permissive- option for users to opt-in into standard conformance mode. And gradually the default was changed for new projects or certain compiler options, as can be read in the documentation:
The /permissive- option is implicitly set by the /std:c++latest option starting in Visual Studio 2019 version 16.8, and in version 16.11 by the /std:c++20 option. /permissive- is required for C++20 Modules support. Perhaps your code doesn't need modules support but requires other features enabled under /std:c++20 or /std:c++latest. You can explicitly enable Microsoft extension support by using the /permissive option without the trailing dash. The /permissive option must come after any option that sets /permissive- implicitly.
By default, the /permissive- option is set in new projects created by Visual Studio 2017 version 15.5 and later versions. It's not set by default in earlier versions. When the option is set, the compiler generates diagnostic errors or warnings when non-standard language constructs are detected in your code. These constructs include some common bugs in pre-C++11 code.
Which brings us to the answer to your question. You weren't seeing it in your original code because you were compiling without /permissive-. In your test project, created in VS2022, /permissive- mode was set by default so it failed to compile. It also fails to compile in an explicit specialization (your void* case) because at that point the template arguments are known and the function is instantiated.
There are a couple of ways to properly fix your code. One is by explicitely deleting the main template and any specialiation you don't want to have.
template<class T> void foo() = delete;
template<> void foo<void*>() = delete;
template<> void foo<int>()
{
// ...
}
This will make any use of the explicitely deleted variants ill-formed, without having the option to include an error message. Of course the void* case is a bit redundant in this example.
If you want to stick to static_assert, you must make the condition for the assert dependent on the template arguments. If you are compiling with C++17, you could do:
template<class...> constexpr bool always_false = false;
template<class T> void foo()
{
static_assert(always_false<T>, "your error here");
}
If using C++14 or earlier, you could wrap the always_false into a struct template:
#include <type_traits>
template<class...> struct always_false : false_type { };
template<class T> void foo()
{
static_assert(always_false<T>::value, "your error here");
}

How to debug template arguments at compile-time?

I have a piece of code that pretty much reduces down to:
template<class T> struct MyStruct; // No definition by default
template<class T> struct MyStruct<T *> { ... }; // Specialization for pointers
Now somewhere in my code, I'm getting an instantiation of MyStruct<T> that happens to be undefined (no C++0x/011, no Boost... nothing fancy, just plain C++03):
error C2027: use of undefined type 'MyStruct<T>'
The trouble is, I have no idea where this is being caused, because the code that's doing the instantiation is itself a template, and called from numerous places, with different arguments.
Is there a way to somehow figure out what T is at compile-time, so I can understand the error messages better?
(Sorry, I forgot to mention: Visual Studio 2008.)
I believe you're using MSVC++, if so, then see the output window, it might have more info printed, especially the line number along with the filename. Once you know the file and line number, you can start from there.
Output window usually prints everything, like how and with what template argument(s), a template is instantiated. Everything step by step. Those messages are very useful when debugging.
As you found yourself, enabling /WL prints more detail messages in the output window.
I know you said no C++11, but you may want to consider, since C++03 code is backwards compatible in all C++11 compliant compilers, to use the static_assert feature of C++11 to debug your code ... if you must do the final compile with a C++03 compiler, then you can always create a #define and use the #ifdef and #endif pre-processor macros to make sure that the static_assert feature does not cause problems in earlier compilers that do not support C++11 features.
See the MSDN docs here for more info.

What does ::template mean other than making TMP compile [duplicate]

This question already has answers here:
Closed 11 years ago.
Possible Duplicate:
Using template parameters as template parameters
Here's a code snippet of some heavily templated container classes used to bind an arbitrary amount of fields of arbitrary types. A co-worker of mine found that my code didn't compile under GCC and after much research he found the fix to get it to deduce the templates correctly by adding ::template ... Neither of us had ever seen this before and still don't really know what this is other than something that GCC needs for my code that Visual Studio 2010 does not need.
template< typename T, int N >
struct SingleBindMemberStruct
{
typedef typename TGenericBindingHandler<T>::BindToUse BindType;
BindType m_Member;
template< typename ContainerClass >
static void AddBinding(CPackedTableDataSpec* spec)
{
// Perhaps with newer versions of the compilers we can find a syntax that both accept. This is with gcc-4.5 and Visual Studio 2010
#if defined(__GNUC__)
TGenericBindingHandler<T>::template AddBinding<ContainerClass>(spec, N, &ContainerClass::template SingleBindMemberStruct<T,N>::m_Member);
#else
TGenericBindingHandler<T>::template AddBinding<ContainerClass>(spec, N, &ContainerClass::SingleBindMemberStruct<T,N>::m_Member);
#endif
}
};
Does anyone know syntactically what ::template can or should be used for? If anyone has a snippet from the standard that describes it that would be perfect!
Edit:
Alright so sounds like it is really as simple as helping the compiler determine what is a template and since this is a static function we use the scope resolution operator rather than the dot operator to tell the compiler of the template. So now the only remaining question is why does Visual Studio not need this as well?
It tells the compiler that AddBinding is a template -- since the definition of AddBinding is dependent on T, the compiler wouldn't otherwise know this at the right stage of the compilation process (I'm no expert on the details, but it's to do with the C++ compilation model AFAIK). By writing template after the ::, you give the compiler information it otherwise wouldn't have. I guess more specifically, it knows that it's dealing with a template and not a < operator when it sees < after AddBinding.
If you want a more detailed answer, you might want to check out C++ Templates: The Complete Guide. (I think it's available as a PDF if you search Google.)
AddBinding is a dependent name, it isn't recognized as template during compilation. Without template it will be interpreted as function pointer and < comparison, which doesn't make sense at all.

Forward-declared type and "non-class type as already been declared as a class type"

I have problem with following code:
template <typename T>
void foo(struct bar & b);
struct bar {};
int main(){}
It compiles successfuly on GCC, but fails on MSVC (2008) with following error:
C2990: 'bar' : non-class type as already been declared as a class type
Is the code wrong or it's a bug in MSVC?
It works if I add struct bar; before template definition.
And we have our winner:
https://connect.microsoft.com/VisualStudio/feedback/details/668430/forward-declared-type-and-non-class-type-as-already-been-declared-as-a-class-type
Thank you for reporting this issue.
This is indeed a case of
non-conformant behaviour in VC++.
However, a simple workaround is to
reorder the declarations so that the
declaration "struct bar" is known when
the template declaration is
encountered. Due to the low severity
of this bug and our priorities, we
regret that we cannot fix the bug in
the next release of the compiler but
we will consider it for a future
release.
Regards,
Tanveer Gani
Visual C++ Team
In most situations, a C (or C++ compiler) works strictly top-to-bottom on your source code. So you need a forward declaration before you ever attempt to reference struct bar, otherwise the compiler will not know that it exists.
Anyway, I've posted a bug in Microsoft Connect: https://connect.microsoft.com/VisualStudio/feedback/details/668430/forward-declared-type-and-non-class-type-as-already-been-declared-as-a-class-type
You most likely have struct bar {}; somewhere above this block of code (possibly in a header file). See http://msdn.microsoft.com/en-us/library/zfcw8kk9.aspx
Edit: Also from the link above:
C2990 can also occur due to a breaking
change in the Visual C++ compiler for
Visual C++ 2005; the compiler now
requires that multiple declarations
for the same type be identical with
respect to template specification.
Since foo is templated and bar is being "forward-declared" in the foo argument list, what happens if you move struct bar {}; above foo?
That looks like valid code. Whatever MSVC is doing, it appears to be some weird non-conforming behavior, from what I can see.