I have the following sample code.
template<class T, class... Args>
T add(T first, Args... rest) {
return first + add(rest...);
}
int add(int a, int b) {
return a + b;
}
When I run it through clang-check -ast-dump, I get the following tree.
FunctionDecl 0x4df0b08 <tmp.cpp:2:1, line:4:1> line:2:3 add 'T (T, Args...)'
|-ParmVarDecl 0x4df0928 <col:7, col:9> col:9 referenced first 'T'
|-ParmVarDecl 0x4df09f8 <col:16, col:24> col:24 referenced rest 'Args...' pack
`-CompoundStmt 0x4df0dd0 <col:30, line:4:1>
`-ReturnStmt 0x4df0dc0 <line:3:5, col:31>
`-BinaryOperator 0x4df0da0 <col:12, col:31> '<dependent type>' '+'
|-DeclRefExpr 0x4df0cd0 <col:12> 'T' lvalue ParmVar 0x4df0928 'first' 'T'
`-CallExpr 0x4df0d78 <col:20, col:31> '<dependent type>'
|-UnresolvedLookupExpr 0x4df0cf0 <col:20> '<overloaded function type>' lvalue (ADL) = 'add' 0x4df0ba8
`-PackExpansionExpr 0x4df0d58 <col:24, col:28> '<dependent type>' lvalue
`-DeclRefExpr 0x4df0d38 <col:24> 'Args' lvalue ParmVar 0x4df09f8 'rest' 'Args...'
FunctionDecl 0x4df0f60 <tmp.cpp:6:1, line:8:1> line:6:5 add 'int (int, int)'
|-ParmVarDecl 0x4df0e00 <col:9, col:13> col:13 used a 'int'
|-ParmVarDecl 0x4df0e80 <col:16, col:20> col:20 used b 'int'
`-CompoundStmt 0x4df10b0 <col:23, line:8:1>
`-ReturnStmt 0x4df10a0 <line:7:5, col:16>
`-BinaryOperator 0x4df1080 <col:12, col:16> 'int' '+'
|-DeclRefExpr 0x4df1010 <col:12> 'int' lvalue ParmVar 0x4df0e00 'a' 'int'
`-DeclRefExpr 0x4df1030 <col:16> 'int' lvalue ParmVar 0x4df0e80 'b' 'int'
I would like to write a matcher for the first case, where one of the arguments of the function is variadic. From the AST Matcher Reference, I have found that there is a isVariadic matcher, though as the documentation says,
Example matches f, but not g or h. The function i will not match, even when
compiled in C mode.
void f(...);
void g(int);
template <typename... Ts> void h(Ts...);
void i();
Is there any way to match the variadic parameter function declaration, and further bind the variadic parameter declaration to some node? I would like to do something like functionDecl(has(parmVarDecl(hasType(packExpansionType().bind("parameter_type"))))), but it seems that this not possible since there is no packExpansionType matcher. Am I missing something here?
For future reference, I found a solution to this. It is actually possible to define custom matchers within a check.
namespace {
const AstTypeMatcher<PackExpansionType> packExpansionType;
const internal::VariadicDynCastAllOfMatcher<Stmt, PackExpansionExpr> packExpansionExpr;
}
// You can also use the macros from ASTMatchersMacros.h
...
Related
I am using protobuf to exchange some messages but when i try to compile the code that uses the messages i am having this conversion error in the repeated field.h file, specifically in the code below.
Is it a version problem ?
.proto file
message mymessage {
repeated double message = 20;
}
protobuf repeated field.h
template <int I>
class FastAdderImpl<I, false> {
public:
explicit FastAdderImpl(RepeatedField* rf) : repeated_field_(rf) {}
void Add(const Element& val) { repeated_field_->Add(val); }
private:
RepeatedField* repeated_field_;
GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(FastAdderImpl);
};
Error:
/usr/local/include/google/protobuf/repeated_field.h:473:17: error: expected ')'
template <int I>
^
/usr/include/complex.h:53:11: note: expanded from macro 'I'
#define I _Complex_I
^
/usr/include/complex.h:48:21: note: expanded from macro '_Complex_I'
#define _Complex_I (__extension__ 1.0iF)
^
/usr/local/include/google/protobuf/repeated_field.h:473:17: note: to match this '('
/usr/include/complex.h:53:11: note: expanded from macro 'I'
#define I _Complex_I
^
/usr/include/complex.h:48:20: note: expanded from macro '_Complex_I'
#define _Complex_I (__extension__ 1.0iF)
^
error: conversion from '_Complex float' to 'int' is not allowed in a converted constant expression
class FastAdderImpl<I, false> {
^
/usr/local/include/google/protobuf/repeated_field.h:474:23: error: conversion from '_Complex float' to 'int' is not allowed in a converted constant expression
class FastAdderImpl<I, false> {
^
/usr/include/complex.h:53:11: note: expanded from macro 'I'
#define I _Complex_I
^~~~~~~~~~
/usr/include/complex.h:48:20: note: expanded from macro '_Complex_I'
#define _Complex_I (__extension__ 1.0iF)
^~~~~~~~~~~~~~~~~~~~~
2 errors generated.
The problem is that a non-type template argument must be a compile-time constant but the type of the non-type template parameter(int) in your example is not the same as the type of the passed argument((__extension__ 1.0iF)) and so requires a conversion.
Perhaps a contrived example might clear this up further:
constexpr float k = 4.4f;
template<int I>
void f()
{
}
int main()
{
//----v----->not valid and will produce a similar error:conversion from 'float' to 'int' in a converted constant expression
f<k>();
}
The above will produce a similar error saying:
conversion from 'float' to 'int' in a converted constant expression
could not convert 'k' from 'const float' to 'int'
Demo
Say I have a dummy class like this:
class Stack
{
public:
template<typename T>
void push(T val)
{ (void)val; }
template<typename T>
T pop()
{ return 0; }
bool empty() const
{ return true; }
};
The AST dump of which looks like:
...
|-CXXRecordDecl 0x5610df147d60 <col:1, col:7> col:7 implicit class Stack
|-AccessSpecDecl 0x5610df147df0 <line:3:1, col:7> col:1 public
|-FunctionTemplateDecl 0x5610df1480e0 <line:4:3, line:6:16> line:5:8 push
| |-TemplateTypeParmDecl 0x5610df147e18 <line:4:12, col:21> col:21 referenced typename depth 0 index 0 T
| `-CXXMethodDecl 0x5610df148040 <line:5:3, line:6:16> line:5:8 push 'void (T)'
| |-ParmVarDecl 0x5610df147f00 <col:13, col:15> col:15 referenced val 'T'
| `-CompoundStmt 0x5610df1484f0 <line:6:3, col:16>
| `-CStyleCastExpr 0x5610df1484c8 <col:5, col:11> 'void' <ToVoid>
| `-DeclRefExpr 0x5610df148498 <col:11> 'T' lvalue ParmVar 0x5610df147f00 'val' 'T'
|-FunctionTemplateDecl 0x5610df148300 <line:8:3, line:10:15> line:9:5 pop
| |-TemplateTypeParmDecl 0x5610df148140 <line:8:12, col:21> col:21 referenced typename depth 0 index 0 T
| `-CXXMethodDecl 0x5610df148260 <line:9:3, line:10:15> line:9:5 pop 'T ()'
| `-CompoundStmt 0x5610df148538 <line:10:3, col:15>
| `-ReturnStmt 0x5610df148528 <col:5, col:12>
| `-IntegerLiteral 0x5610df148508 <col:12> 'int' 0
`-CXXMethodDecl 0x5610df1483e0 <line:12:3, line:13:18> line:12:8 empty 'bool () const'
`-CompoundStmt 0x5610df148570 <line:13:3, col:18>
`-ReturnStmt 0x5610df148560 <col:5, col:12>
`-CXXBoolLiteralExpr 0x5610df148550 <col:12> 'bool' true
Given access to the CXXRecordDecl node for Stack, how can I access the FunctionTemplateDecl objects for push and pop? CXXRecorDecl::methods only returns the method declaration for empty and I don't see any other functions that might be helpful. Do I have to match the function template declarations separately and then later associate them with the class declaration?
I believe I have now figured it out, I will answer my own question in case this is useful for someone else:
The way to do this is to first cast the given CXXRecordDecl to DeclContext, iterate over the contained declarations and try to cast them to FunctionTemplateDecl, pseudocode:
clang::CXXRecordDecl const *record = // ...
for (auto const *inner : static_cast<clang::DeclContext>(record)->decls()) {
auto const *member_function_template = llvm::dyn_cast<clang::FunctionTemplateDecl>(inner);
if (!member_function_template)
continue;
// do something useful
}
I have a vector of Hill structs and want to find the one with the heighest height. Here's my code:
#include <vector>
#include <algorithm>
#include <assert.h>
struct Hill {
int height;
int changed;
};
int main() {
std::vector<Hill> hills(100);
hills[0].height = 100;
hills[1].height = 150;
auto byHeight = [&](const Hill& a, const Hill& b) {
return a.height < b.height;
};
Hill hill = std::max(hills.begin(), hills.end(), byHeight);
assert(hill.height == 150);
}
But it fails to compile:
mcve.cpp:15:10: error: no viable conversion from 'const
std::__1::__wrap_iter<Hill *>' to 'Hill'
Hill hill = std::max(hills.begin(), hills.end(), byHeight);
^ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
mcve.cpp:4:8: note: candidate constructor (the implicit copy constructor) not
viable: no known conversion from 'const std::__1::__wrap_iter<Hill *>' to
'const Hill &' for 1st argument
struct Hill {
^
mcve.cpp:4:8: note: candidate constructor (the implicit move constructor) not
viable: no known conversion from 'const std::__1::__wrap_iter<Hill *>' to
'Hill &&' for 1st argument
struct Hill {
^
In file included from mcve.cpp:1:
In file included from /Library/Developer/CommandLineTools/usr/include/c++/v1/vector:270:
In file included from /Library/Developer/CommandLineTools/usr/include/c++/v1/__bit_reference:15:
/Library/Developer/CommandLineTools/usr/include/c++/v1/algorithm:2627:12: error:
no matching function for call to object of type '(lambda at
mcve.cpp:12:21)'
return __comp(__a, __b) ? __b : __a;
^~~~~~
mcve.cpp:15:22: note: in instantiation of function template specialization
'std::__1::max<std::__1::__wrap_iter<Hill *>, (lambda at mcve.cpp:12:21)>'
requested here
Hill hill = std::max(hills.begin(), hills.end(), byHeight);
^
mcve.cpp:12:21: note: candidate function not viable: no known conversion from
'const std::__1::__wrap_iter<Hill *>' to 'const Hill' for 1st argument
auto byHeight = [&](const Hill& a, const Hill& b) {
^
2 errors generated.
How do I fix it?
Changing these two lines of code fixed the issue (thanks to #milleniumbug):
auto hill = std::max_element(hills.begin(), hills.end(), byHeight);
assert(hill->height == 150);
*std::max_element gets you the element itself. std::max_element returns an iterator, giving you an opportunity to modify the element.
Change std::max to *std::max_element and it'll work. With the *.
max_element() returns an iterator, which * dereferences to get the actual element.
I'm beginner with libtooling, I try to learn with easy c++ code. I try to parse/print typedef expression like below lines:
namespace DEBUG {
typedef void(*function_pointer_t)(int&);
typedef int myInt;
}
clang++ -Xclang -ast-dump -fsyntax-only output:
`-NamespaceDecl 0x3e4fe331a8 <test.h:3:1, line:7:1> line:3:11 TESTS
|-TypedefDecl 0x3e4fe333b8 <line:4:2, col:42> col:18 function_pointer_t 'double (*)(int &)'
| `-PointerType 0x3e4fe33350 'double (*)(int &)'
| `-ParenType 0x3e4fe332f0 'double (int &)' sugar
| `-FunctionProtoType 0x3e4fe332b0 'double (int &)' cdecl
| |-BuiltinType 0x3e4fe32b60 'double'
| `-LValueReferenceType 0x3e4fe33210 'int &'
| `-BuiltinType 0x3e4fe32a40 'int'
`-TypedefDecl 0x3e4fe33420 <line:5:2, col:14> col:14 myInt 'int'
`-BuiltinType 0x3e4fe32a40 'int'
To parse it I create a class that inherite from MatchFinder::MatchCallback and overload MatchFinder::MatchCallback::run:
class TypdefDeclFinder : public MatchFinder::MatchCallback {
public:
virtual void run(const MatchFinder::MatchResult& result)
{
auto Item = result.Nodes.getNodeAs<clang::TypedefDecl>("typedefDeclMatch");
if (!Item) return;
if (!IsDeclFromInputFiles(Item, result.SourceManager)) return;
if (!Item->getIdentifier()) return;
if (IsInsideTemplateContext(Item)) return;
print(Item);
}
};
But the Item pointer is equal to null. I can parse/print function, variable, class, struct template, method templates, enum ... with MacthFinder::MatchCallback, but this way doesn't run on typedef.
What's wrong with this code?
I'm writing a delegate class but it fails to take const member functions.
Here is a test case :
class foo
{
public:
void MemberFunction()
{
printf("non const member function\n");
}
void ConstMemberFunction() const
{
printf("const member function\n");
}
};
template <class C, void (C::*Function)()>
void Call(C* instance)
{
(instance->*Function)();
}
int main (int argc, char** argv)
{
foo bar;
Call<foo,&foo::MemberFunction>(&bar);
Call<foo,&foo::ConstMemberFunction>(&bar);
}
Now the compiler (visual studio 2010) gives me an error he cannot convert the const member function to a non-const function :
2>..\src\main.cpp(54): error C2440: 'specialization' : cannot convert from 'void (__cdecl foo::* )(void) const' to 'void (__cdecl foo::* const )(void)'
2> Types pointed to are unrelated; conversion requires reinterpret_cast, C-style cast or function-style cast
2>..\src\main.cpp(54): error C2973: 'Call' : invalid template argument 'void (__cdecl foo::* )(void) const'
2> ..\src\main.cpp(37) : see declaration of 'Call'
ok, easy fix (I though :P ) by adding this :
template <class C, void (C::*Function)() const>
void Call(C* instance)
{
(instance->*Function)();
}
but now the compiler is completly confused (and me with it). it looks like he now tries to use the const function for the non-const member function and the non-const function for the const member function.
2>..\src\main.cpp(53): error C2440: 'specialization' : cannot convert from 'void (__cdecl foo::* )(void)' to 'void (__cdecl foo::* const )(void) const'
2> Types pointed to are unrelated; conversion requires reinterpret_cast, C-style cast or function-style cast
2>..\src\main.cpp(53): error C2973: 'Call' : invalid template argument 'void (__cdecl foo::* )(void)'
2> ..\src\main.cpp(43) : see declaration of 'Call'
2>..\src\main.cpp(53): error C2668: 'Call' : ambiguous call to overloaded function
2> ..\src\main.cpp(43): could be 'void Call<foo,void foo::MemberFunction(void)>(C *)'
2> with
2> [
2> C=foo
2> ]
2> ..\src\main.cpp(37): or 'void Call<foo,void foo::MemberFunction(void)>(C *)'
2> with
2> [
2> C=foo
2> ]
2> while trying to match the argument list '(foo *)'
2>..\src\main.cpp(54): error C2440: 'specialization' : cannot convert from 'void (__cdecl foo::* )(void) const' to 'void (__cdecl foo::* const )(void)'
2> Types pointed to are unrelated; conversion requires reinterpret_cast, C-style cast or function-style cast
2>..\src\main.cpp(54): error C2973: 'Call' : invalid template argument 'void (__cdecl foo::* )(void) const'
2> ..\src\main.cpp(37) : see declaration of 'Call'
2>..\src\main.cpp(54): error C2668: 'Call' : ambiguous call to overloaded function
2> ..\src\main.cpp(43): could be 'void Call<foo,void foo::ConstMemberFunction(void) const>(C *)'
2> with
2> [
2> C=foo
2> ]
2> ..\src\main.cpp(37): or 'void Call<foo,void foo::ConstMemberFunction(void) const>(C *)'
2> with
2> [
2> C=foo
2> ]
2> while trying to match the argument list '(foo *)'
If I would rename the second Call function (with the const), it all works fine but I would rather use one function.
So, can anybody point me towards what I'm doing wrong and how I can make this work ?
Thx!
I think you might be able to address this by removing the function pointer from the template type signature and instead relying on overloading:
template <class C>
void Call(C* ptr, void (C::*function)()) {
(ptr->*function)();
}
template <class C>
void Call(C* ptr, void (C::*function)() const) {
(ptr->*function)();
}
This now uses normal function overloading to select which of the two functions should be called. const member function pointers will call down to the second version, while non-const functions will call up to the first version. This also means that you don't need to explicitly provide any type information to the template function; the compiler can deduce C in both contexts.
Let me know if (1) this doesn't work or (2) this does work, but isn't what you want.
Hope this helps!
Use std::bind or lambdas, which VS2010 supports, and std::function and this will cease to be a problem for you.
Your problem is that a member function pointer is a different type than a const member function pointer. I modified your Call function to this:
template< typename C, typename funcptr_t, funcptr_t ptr >
void Call( C *instance )
{
(instance->*ptr)();
}
and now using the additional template parameter, I can call it like this:
Call<foo,void (foo::*)(),&foo::MemberFunction>(&bar);
Call<foo,void (foo::*)() const, &foo::ConstMemberFunction>(&bar);
That's a bit messy, though. The overloading solution is better! :-)