irony-mode icon indicating copy to clipboard operation
irony-mode copied to clipboard

Question about C++11/14/17 auto completion

Open mambolevis opened this issue 8 years ago • 25 comments

Hi Sarcasm,

Are irony able to make autocompletion of all C++11/14/17 features?

Thanks,

Levis

mambolevis avatar Feb 18 '17 23:02 mambolevis

It should work if the appropriate -std= option is given and if the feature is supported by your libclang version

Sarcasm avatar Feb 19 '17 08:02 Sarcasm

I'm using clang 3.9.1 the latest one. When I try to use Template argument deduction for class templates:

template<typename T = float>
struct MyContainer
{
    T val;
	MyContainer() : val(){}
	MyContainer(T val) : val (val){}
};
MyContainer c1 {1};
MyContainer c2;

Flycheck reports two errors: 27 1 error use of class template 'MyContainer' requires template arguments (irony) 28 1 error use of class template 'MyContainer' requires template arguments (irony)

I checked the clang support status for C++17 (http://clang.llvm.org/cxx_status.html) and I found that it is supported at the SVN repository and using the flag -std=c++1z. As windows user, I always take the binaries offered at http://releases.llvm.org/download.html#svn.

Which version of clang are you using, can you checked if Flycheck also reports the same to you? Thanks,

mambolevis avatar Feb 19 '17 22:02 mambolevis

Interesting, I wasn't aware of this feature yet. the llvmweekly from today (http://llvmweekly.org/issue/164) says:

C++1z class template argument deduction is now considered 'done'. r295011.

So I guess this is pretty recent.

I built clang and libclang from today's ToT.

$ ./bin/irony-server --version
irony-server version 0.2.2-cvs
clang version 5.0.0

Once I setup the -std=c++1z flag flycheck-irony reports the error:

 error: declaration of variable 'c2' with deduced type 'MyContainer' requires an initializer (irony)

c1 is "compiling" fine.

This feature does not work with my system version of clang which is the 3.9.1 version. Since this is the version currently available for download at http://releases.llvm.org/download.html, I think you have to build from trunk yourself if you want the feature today.

Sarcasm avatar Feb 20 '17 19:02 Sarcasm

Ok, good news. Questions:

  1. Did you build the whole LLMV?
  2. Where can I get LLMV latest version (the CVS source code) ?

I am not quite sure if it is possible to build LLMV in Windows using CVS. I tried long time ago but I couldn't build it. For that reason, I also take the latest pre-built binaries.

In this link you ca find more C++11/14/17 features with example: https://github.com/AnthonyCalandra/modern-cpp-features

mambolevis avatar Feb 20 '17 21:02 mambolevis

In the past, 2013, LLVM wasn't too difficult to build on Windows. I used CMake + Ninja (like on Linux). Today, I'd like to think this is even easier.

Anyway, it also looks like there are snapshot builds available here:

  • http://llvm.org/builds/

For example, today they have an a Windows installer, based on SVN r293544 (30 January 2017). If it contains libclang maybe you are already good.

Sarcasm avatar Feb 20 '17 21:02 Sarcasm

Thank you.

Changing a little bit the topic. How do you detect when a .cpp or .h file is loaded in order to set up, for instance, the appropriated .clang_complete flags?

I am using the following hook (add-hook 'change-major-mode-hook #'module-headers) to handle the problem. If a .h file is loaded the hook call the module-headers function. The problem is that this hook is activated when a change in any emacs major mode occurs.

Is there a hook that just considers changes on c++-mode andc-modeor do you have any suggestion how to run a function when a .cpp or a .h file is loaded?

mambolevis avatar Feb 20 '17 23:02 mambolevis

I'm not sure to understand the question.

I you look at irony-mode's configuration section, you have (trimmed down version):

(add-hook 'c++-mode-hook 'irony-mode)
(add-hook 'irony-mode-hook 'irony-cdb-autosetup-compile-options)

This says:

  1. enable irony-mode when a C++ buffer is opened
  2. load the compilation database when irony-mode is started (which is when a C++ buffer is opened)

Sarcasm avatar Feb 20 '17 23:02 Sarcasm

I'm not sure to understand the question

When a C++ buffer is opened, I search and read its associated .clang_complete to extract the paths where the headers are located. I use this information to setup company-c-headers. This is the reason why I use (add-hook 'change-major-mode-hook #'module-headers). When many buffers are opened, the hook is executed once a buffer is selected buffer. It is working, but I don't know if there is a more efficient way to do it.

mambolevis avatar Feb 21 '17 07:02 mambolevis

Still not exactly sure. What is the advantage of change-major-mode-hook over c++-mode-hook here?

Sarcasm avatar Feb 21 '17 08:02 Sarcasm

I checked my function today and I'm already using the c++-mode-hook . I commented ;;(add-hook 'change-major-mode-hook #'module-headers)

(add-hook 'c-mode-hook #'module-headers)
(add-hook 'c++-mode-hook #'module-headers)
;;(add-hook 'change-major-mode-hook #'module-headers)

Let me try to explain the problem I am facing:

  1. If I open a c++ buffer from project A, the function module-headers is called and its associated .clang_complete is read without problems.

  2. Now, I open other c++ buffer from project B, the function module-headers is called and its respective .clang_complete is read without problems.

3. If I come back and select the c++ buffer form project A, the function module-headers in not called.

  1. If I kill the c++ buffer form project A and reopen it again, the function module-headers is called and its respective .clang_complete is read without problems.

Do you have any Idea how the problem (scenario 3 is where the problem is located). Maybe there is a way to execute the function when the focus changes among c++ buffers?

mambolevis avatar Feb 21 '17 23:02 mambolevis

I don't think there is any problem with the time the function is called, it only depends on the definition of the function. Would you mind sharing the code of this module-headers function so I can eventually propose an educated on the first try?

Sarcasm avatar Feb 21 '17 23:02 Sarcasm

The function bellow. I'm not a lisp expert as you can see. Please, let me know if I can improve it.

(defun module-headers ()
    "Setup 'company-c-headers-path-user."
    ;;(message (format "%s" major-mode))
    (when (or (equal major-mode 'c++-mode)
              (equal major-mode 'minibuffer-inactive-mode)
              (equal major-mode 'c-mode))
      (setq company-c-headers-path-user nil)
      (with-temp-buffer
        (while (and (not (file-exists-p ".clang_complete"))
                    (not (equal ":/" (substring default-directory -2))))
          (cd ".."))
        (if (file-exists-p ".clang_complete")
            (progn
              (let ((file-content-list)
                    (length-item)
                    (ini-ref-path))
                ;;(message "Test: %s" default-directory)
                (setq file-content-list
                      (read-lines (concat default-directory ".clang_complete")))
                (while file-content-list
                  (setq length-item (length (car file-content-list)))
                  (when (> length-item 4)
                    (when (equal (substring (car file-content-list) 0 4) "-I./")
                      (add-to-list 'company-c-headers-path-user
                                   (concat default-directory
                                           (substring (car file-content-list) 4 length-item))))
                    (when (equal (substring (car file-content-list) 0 5) "-I../")
                      (setq ini-ref-path default-directory)
                      (cd "../")
                      (add-to-list 'company-c-headers-path-user
                                   (concat default-directory
                                           (substring (car file-content-list) 5 length-item)))
                      (cd ini-ref-path)))
                  (setq file-content-list (cdr file-content-list)))))))))

mambolevis avatar Feb 21 '17 23:02 mambolevis

Many ways to improve! :)

Not thoroughly tested and using unsupported irony-mode functions (but they won't disappear tomorrow), but straightforward and should not exhibit the same issues as what you described:

(defun company-c-headers-path-user-irony ()
  "Return the user include paths for the current buffer."
  (when irony-mode
    (irony--extract-user-search-paths irony--compile-options
                                       irony--working-directory)))

(setq company-c-headers-path-user 'company-c-headers-path-user-irony)

Also note that there is a company-irony-c-headers which might already do the work.

Sarcasm avatar Feb 21 '17 23:02 Sarcasm

Thank you for your feedback. I will test both tomorrow. Now I am using my tablet. What do you use for header completion?

mambolevis avatar Feb 21 '17 23:02 mambolevis

Nothing... I should probably use one or integrate this into irony-mode (so that would simplify the company config). A long time ago irony was providing this feature but it did not survive some refactoring.

Sarcasm avatar Feb 21 '17 23:02 Sarcasm

I tested both options and the first one, company-c-headers-path-user-irony, solves my problem. The second option using company-irony-c-headers did not work.

How do you control when the focus change among C++ buffers? I would like to implement this functionality in my function module-headers to learn more about Emacs lisp.

mambolevis avatar Feb 22 '17 19:02 mambolevis

Oh right, I didn't even explain. You don't have to do anything when the focus change. Emacs has support for 'buffer local variable': https://www.gnu.org/software/emacs/manual/html_node/elisp/Buffer_002dLocal-Variables.html

The irony--compile-options variable is a buffer local variable, which means there are as many "instance" of this variable as there is buffers, each buffer can give the variable a different value.

To set this variable for the *scratch* buffer programmatically for example, you could do:

(with-current-buffer "*scratch*"
  (setq irony--compile-options 1))

(with-current-buffer "*Messages*"
  (setq irony--compile-options 2))

(dolist (buf-name '("*scratch*" "*Messages*"))
  (with-current-buffer buf-name
    (message "%s: %s" (buffer-name) irony--compile-options))
  (sleep-for 1))

In your case, the variable company-c-headers-path-user is not buffer local by default, so you need to make this variable buffer local.

There are a few ways to do this, they are explained here:

  • https://www.gnu.org/software/emacs/manual/html_node/elisp/Creating-Buffer_002dLocal.html

You may also be interested by http://stackoverflow.com/a/21919451/951426

Don't hesitate to share what you end up with, I can review it if you want. :+1:

Sarcasm avatar Feb 22 '17 21:02 Sarcasm

Have either of you have a problem with "auto" in something like:


std::vector<std::string> theVec = { "one", "two", "three" };
std::for_each( theVec.begin(), theVec.end(), [&]( auto& ref ) { if ( ref.size() > 3 ) std::cout << "THREE!!" << std::endl; } );

I cannot seem to get completions on auto for this even when I hack in -std=c++14 in the .clang_complete.

Cheers.

@mambolevis

3246251196 avatar Aug 03 '17 12:08 3246251196

Hi,

is this what you are searching for? image

mambolevis avatar Aug 03 '17 13:08 mambolevis

Sorry, I mean that for some reasons I do not get completions on 'ref.' It seems that clang does not know how to resolve the auto type.

So, 'ref.' should give me completions for a std::string.

3246251196 avatar Aug 03 '17 13:08 3246251196

I have the following message: 'auto' not allowed in lambda parameter (irony) from flycheck errors I also don't get completions from ref.

mambolevis avatar Aug 03 '17 14:08 mambolevis

Using the following flag -std=c++1z the message from flycheck disappears. https://clang.llvm.org/cxx_status.html

mambolevis avatar Aug 03 '17 14:08 mambolevis

Thanks. I still cannot get completion on "ref" with c++1z, I will have to look into this a little later though.

3246251196 avatar Aug 03 '17 14:08 3246251196

Please, let me know if you find something.

mambolevis avatar Aug 03 '17 14:08 mambolevis

I have the same behavior, and I think it's understandable that Clang does not get it right. Doing M-x irony-get-type RET, shows a type of type-parameter-0-0, which looks like a generated type name for the template argument.

IIUC, the generic lambda is converted to a template function/functor. With template we don't know the types of the argument until they are instantiated. I believe libclang only sees a template function/functor, but does not see that it is instanciating it as well, which is where it would have the information about the type of ref.

The right thing to do if you want this to be fixed is to propose a patch to the Clang folks. The next best thing, and with lower barrier to entry, is to submit an issue to their bug tracker, but you won't have the guarantee that someone will work on it anytime soon, but it's the right thing to do.

Sarcasm avatar Aug 03 '17 23:08 Sarcasm