How to find move constructors in codebase using Clang AST tools? - c++

Following up a comment from this question: how can I find move constructors in C++ codebase using Clang AST tools? (find definitions / declarations only)

The Clang AST matcher now provides this functionality with the isMoveConstructor matcher. Here's an example program:
#include <iostream>
#include "clang/AST/AST.h"
#include "clang/ASTMatchers/ASTMatchers.h"
#include "clang/ASTMatchers/ASTMatchFinder.h"
#include "clang/Basic/SourceManager.h"
#include "clang/Basic/SourceLocation.h"
#include "clang/Tooling/CommonOptionsParser.h"
#include "clang/Tooling/Refactoring.h"
static llvm::cl::OptionCategory ToolingSampleCategory("move constructor finder");
struct MoveCtorHandler : public clang::ast_matchers::MatchFinder::MatchCallback {
public:
virtual void run(clang::ast_matchers::MatchFinder::MatchResult const& result) override {
using namespace clang;
// reject things from include files
ASTContext *ctx = result.Context;
const CXXConstructorDecl *decl = result.Nodes.getStmtAs<CXXConstructorDecl>("moveCtor");
auto loc = decl->getLocation();
if (!ctx->getSourceManager().isInMainFile(loc)) {
return;
}
std::cout << "found a move constructor at "
<< loc.printToString(ctx->getSourceManager())
<< std::endl;
}
};
int main(int argc, char const **argv) {
using namespace clang;
using namespace clang::tooling;
using namespace clang::ast_matchers;
CommonOptionsParser opt(argc, argv, ToolingSampleCategory);
RefactoringTool tool(opt.getCompilations(), opt.getSourcePathList());
MatchFinder finder;
// set up callbacks
MoveCtorHandler move_ctor_handler;
finder.addMatcher(constructorDecl(isMoveConstructor()).bind("moveCtor"),
&move_ctor_handler);
if (int result = tool.run(newFrontendActionFactory(&finder).get())) {
return result;
}
return 0;
}
When applied to the following input:
#include <vector>
struct foo {
foo() {}
foo(foo && other) : v_(std::move(other.v_)) {}
private:
std::vector<int> v_;
};
int main() {
}
It produces the output:
found a move constructor at xxx.cpp:5:5

Related

C++ (14) - googletest undefined identifier

I'm using c++ 14. Why isn't googletest able to pick up the curl_client class object pointer? Did I initialize it correctly in CurlClientTest?
Testing code:
#include "../src/include/CurlClient.h"
#include <gtest/gtest.h>
#include <string>
class CurlClientTest : testing::Test {
public:
SimpleCURLClient::CurlClient *curl_client;
virtual void SetUp() {
curl_client = new SimpleCURLClient::CurlClient(test_url);
}
virtual void TearDown() {
delete curl_client;
}
private:
std::string test_url = "http://postman-echo.com/get?foo1=bar1&foo2=bar2";
};
TEST(CurlClientTest, CurlClientInitTest) {
std::cout << curl_client->getCurlUrl << "\n";
}
code for CurlClient.h:
#include <curl/curl.h>
#include <exception>
#include <iostream>
#include <sstream>
#include <string>
#include <utility>
#include <vector>
#ifndef UTILS_CURLCLIENT_H
#define UTILS_CURLCLIENT_H
namespace SimpleCURLClient {
class CurlClient {
public:
CurlClient(std::string remote_url, int ip_protocol = 1, int timeout = 10,
bool follow_redirects = 1);
~CurlClient();
void setCurlUrl(std::string &new_url);
std::string getCurlUrl();
void setOption(CURLoption curl_option_command, long curl_option_value);
void setOption(CURLoption curl_option_command, std::string curl_option_value);
void setHeader(std::vector<std::string> &header_list);
std::pair<CURLcode, std::string> makeRequest();
std::pair<CURLcode, std::string> makeRequest(std::string &post_params);
std::pair<CURLcode, std::string> sendGETRequest();
std::pair<CURLcode, std::string> sendPOSTRequest(std::string &post_params);
std::pair<CURLcode, std::string> sendDELETERequest(std::string &post_params);
private:
std::string m_curl_url;
CURL *m_curl;
struct curl_slist *m_curl_header_list;
};
} // namespace SimpleCURLClient
#endif // UTILS_CURLCLIENT_H
Error:
Build FAILED.
"E:\somepath\simple_curl_cpp\build\test\simple_curl_cpp_test.vcxproj" (default target) (1) ->
(ClCompile target) ->
E:\somepath\simple_curl_cpp\test\CurlClientTest.cc(21): error C2065: 'curl_client': undeclared identifier [E:\somepath\simple_curl_cpp\build\test\simple_curl_cpp_test.vcxproj]
E:\somepath\simple_curl_cpp\test\CurlClientTest.cc(21): error C2227: left of '->getCurlUrl' must point to class/struct/union/generic type [E:\somepath\simple_curl_cpp\build\test\simple_curl_cpp_test.vcxproj]
ANSWER (GIVEN BY Chris Olsen in comments) :
We have to use TEST_F and NOT TEST. Also change CurlClientTest to public. The below code for the test works.
#include "../src/include/CurlClient.h"
#include <gtest/gtest.h>
#include <string>
class CurlClientTest : public testing::Test {
public:
SimpleCURLClient::CurlClient *curl_client;
virtual void SetUp() {
curl_client = new SimpleCURLClient::CurlClient(test_url);
}
virtual void TearDown() {
delete curl_client;
}
private:
std::string test_url = "http://postman-echo.com/get?foo1=bar1&foo2=bar2";
};
TEST_F(CurlClientTest, CurlClientInitTest) {
std::cout << curl_client->getCurlUrl() << "\n";
}
Tests using fixtures require use of the TEST_F macro. See Test Fixtures in the Google Test Primer for more info.
TEST_F(CurlClientTest, CurlClientInitTest) {
std::cout << curl_client->getCurlUrl << "\n";
}

How to use Clang LibTooling multiple times

Minimul source that use Clang LibTooling which is a very common way:
#include "pch.h"
#include "clang/Frontend/FrontendActions.h"
#include "clang/Tooling/CommonOptionsParser.h"
#include "clang/Tooling/Tooling.h"
#include "llvm/Support/CommandLine.h"
#include "clang/Driver/Options.h"
#include "clang/AST/AST.h"
#include "clang/AST/ASTContext.h"
#include "clang/AST/ASTConsumer.h"
#include "clang/AST/RecursiveASTVisitor.h"
#include "clang/Frontend/ASTConsumers.h"
#include "clang/Frontend/FrontendActions.h"
#include "clang/Frontend/CompilerInstance.h"
#include "clang/Tooling/CommonOptionsParser.h"
#include "clang/Tooling/Tooling.h"
#include "clang/Rewrite/Core/Rewriter.h"
#include <iostream>
using namespace std;
using namespace clang;
using namespace clang::driver;
using namespace clang::tooling;
using namespace llvm;
class ExampleVisitor : public RecursiveASTVisitor<ExampleVisitor> {
public:
explicit ExampleVisitor(CompilerInstance *CI) {}
};
class ExampleASTConsumer : public ASTConsumer {
private:
CompilerInstance *CI;
public:
explicit ExampleASTConsumer(CompilerInstance *CI) : CI(CI) {}
virtual void HandleTranslationUnit(ASTContext &Context) {
ExampleVisitor(CI).TraverseDecl(Context.getTranslationUnitDecl());
}
};
class ExampleFrontendAction : public ASTFrontendAction {
public:
virtual std::unique_ptr<ASTConsumer> CreateASTConsumer(CompilerInstance &CI, StringRef file) {
return std::unique_ptr<ASTConsumer>(new ExampleASTConsumer(&CI));
}
};
void run(int argc, const char **argv, llvm::cl::OptionCategory& tc) {
CommonOptionsParser op(argc, argv, tc);
ClangTool Tool(op.getCompilations(), op.getSourcePathList());
std::cout <<"getSourcePathList.size="<< op.getSourcePathList().size()<<"\n";
int result = Tool.run(newFrontendActionFactory<ExampleFrontendAction>().get());
}
int main(int argc, const char **argv) {
llvm::cl::OptionCategory tc1("c1");
llvm::cl::OptionCategory tc2("c2");
llvm::cl::OptionCategory tc3("c3");
run(argc, argv,tc1);
run(argc, argv,tc2);
run(argc, argv,tc3);
std::cin.get();
return 0;
}
the parameters to debug the application is:
"the_only_source_file_to_scan.cpp" --
which is fine.
The output is (from the method "run" above main()):
getSourcePathList.size=1
getSourcePathList.size=2
getSourcePathList.size=3
The problem is that main() calls run() 3 times with the same above parameter which contains only 1 source file to scan, but each time the size of the source-to-scan list stored in CommonOptionsParser increase by one (every item in the list is the same file input from argv), it just seems to append the source file to scan to the list each time.
Everything above is saved in newly created temporary variables in each run, then how and why does LibTooling keep states of the last run and how to "reset" these states?
use FixedCompilationDatabase could circumvent this problem, it can run multiple clangTool in one process
The above code use CommonOptionsParser, whose code is in
clang\lib\Tooling\CommonOptionsParser.cpp
in the method CommonOptionsParser::init there is:
static cl::list<std::string> SourcePaths(...);
Each invocation will add its sources to this static variable. So it is this local static variable that cause the memory of the previous invocations. In each invocation this local static variable is modified by cl::ParseCommandLineOptions in some unkown way since it is not passed into cl::ParseCommandLineOptions at all. After SourcePaths is modified (i.e. sources of the current invocation are added to SourcePaths which may already contain sources of previous invocations), it is finally copied into a instance variable.
BTW #AbaoZhang's clue helps me find the location, indeed CommonOptionsParser::init use FixedCompilationDatabase interally but not for the above situation.
static cl::list<std::string> SourcePaths(...);
#jw_ Yes This code makes problem.
You can fix this.
for (auto iter = llvm::cl::AllSubCommands->OptionsMap.begin(); iter != llvm::cl::AllSubCommands->OptionsMap.end(); iter++)
{
iter->getValue()->setDefault();
}
for (auto iter = llvm::cl::AllSubCommands->PositionalOpts.begin(); iter != llvm::cl::AllSubCommands->PositionalOpts.end(); iter++)
{
(*iter)->setDefault();
}
for (auto iter = llvm::cl::AllSubCommands->SinkOpts.begin(); iter != llvm::cl::AllSubCommands->SinkOpts.end(); iter++)
{
(*iter)->setDefault();
}

setter and getter /classes

I have a class Project and each Project can have different tasks.
Project.h:
#pragma once
#include "Task.h"
#include <vector>
using namespace std;
class Project
{
private:
vector<Task> Tasks;
public:
Project::Project(int inleesgetal);//constructor
vector<Task> GetTasks();
};
Project.cpp:
#include "Project.h"
#include <string>
#include <vector>
Project::Project(int inleesgetal)
{
//constructor
Tasks.resize(Numbertasks);
}
vector<Task> Project::GetTasks()
{
return Tasks;
}
Task.h:
#pragma once
#include <vector>
using namespace std;
class Task
{
private:
//Info:
int StartTime_Solo;
public:
Task(); //constructor
void SetStartTime_Solo(int st_s);
int GetStartTime_Solo();
};
Task.cpp:
#include "Task.h"
#include <string>
#include <vector>
#include <iostream>
using namespace std;
Task::Task()
{
//constructor
StartTime_Solo = 0;
}
int Task::GetStartTime_Solo()
{
return StartTime_Solo;
}
void Task::SetStartTime_Solo(int st_s)
{
StartTime_Solo = st_s;
}
main:
#include <iostream>
#include <vector>
#include "Task.h"
#include "Project.h"
using namespace std;
int main()
{
Project Project1(6);
Project1.GetTasks()[2].SetStartTime_Solo(55);
cout << "test:" << Project1.GetTasks()[2].GetStartTime_Solo();
return 0;
}
Now when I try to set the 3rd task of Project1 to a starttime of 55 and then print the start time out it still gives me 0 as a result.
Why is this? And how can I change my code so it actually sets the starttime to 55?
vector<Task> GetTasks();
should be
const vector<Task>& GetTasks() const;
vector<Task>& GetTasks();
And so with definitions:
vector<Task> Project::GetTasks()
{
return Tasks;
}
should be:
const vector<Task>& Project::GetTasks() const { return Tasks; }
vector<Task>& Project::GetTasks() { return Tasks; }
The problem is that you are returning a copy of the vector<Task> from the GetTasks function. You then modify this copy and throw it away right afterwards. The internal member of Project is not changed.
If you return by reference like this:
vector<Task>& GetTasks();
Then you are basically returning something that points to the internal vector, and so when you modify it, you actually modify the member data of your class.

How do I call a class by passing it's object and member function to another function in c++?

How do I execute a member's function by passing the object and the member's function to another function in c++. I do understand the answer to my question is out there; however, I do not know what this is called. So far I created 2 files, exeFunc.h and exeFunc.cpp. Their code consist of:
exeFunc.h
/*
File: exeFunc.h
Header file for exeFunc Library.
*/
#ifndef EXEFUNC_H
#define EXEFUNC_H
#include "mbed.h"
#include "msExtensions.h"
#include "cfExtensions.h"
#include <map>
class exeFunc
{
public:
exeFunc(msExtensions &msExt, cfExtensions &cfExt);
private:
void _splitFuncFromCmd();
void _attachCallback();
msExtensions &_msExt;
cfExtensions &_cfExt;
//FunctionPointer _p;
};
#endif
exeFunc.cpp
/*
File: exeFunc.cpp
Execute functions in other Sensor libraries/classes
Constructor
*/
#include "mbed.h"
#include "ConfigFile.h"
#include "msExtensions.h"
#include "cfExtensions.h"
#include "exeFunc.h"
#include <map>
#include <string>
using namespace std;
exeFunc::exeFunc(msExtensions &msExt, cfExtensions &cfExt) : _msExt(msExt), _cfExt(cfExt)
{
//_cfExt.checkConfigForFirstStart();
//_p.attach(&_cfExt, &cfExtensions::checkConfigForFirstStart);
//_p.call();
}
void exeFunc::_splitFuncFromCmd()
{
}
void exeFunc::_attachCallback()
{
}
I wrote a completed example, may helps
class MyClass
{
public:
MyClass(int b)
:_b(b)
{
}
int Foo(int a)
{
return a * _b;
}
int _b;
};
typedef int (MyClass::*MFP)(int);
int get_result(MyClass* obj, MFP mfp)
{
int r = (obj->*mfp)(5); // 30
return r;
}
int _tmain(int argc, _TCHAR* argv[])
{
MFP mfp = &MyClass::Foo;
MyClass m(6);
get_result(&m, mfp);
return 0;
}
You call it by another function.if you have an independent function.
To be honesty your question is not completely clear.However :
int F(int,int,int);
int g();
//main scope
F(g(),a,b)

declaring a vector as a class member

I have simple class in a header file: a.hh
#ifndef a_hh
#define a_hh
class a
{
public:
int i;
a()
{
i = 0;
}
};
#endif
Then i have a file:b.cc
#include <iostream>
#include "a.hh"
using namespace std;
int main(int argc, char** argv)
{
a obj;
obj.i = 10;
cout << obj.i << endl;
return 0;
}
>
Till this point everything is fine.
I compile the code and it compiles fine.
But as soon as i add a vector in the class:
#ifndef a_hh
#define a_hh
class a
{
public:
int i;
vector < int > x;
a()
{
i = 0;
}
};
#endif
I get a compilation error as below:
> CC b.cc
"a.hh", line 7: Error: A class template name was expected instead of vector.
1 Error(s) detected.
What is the problem with declaring a vector here as a member?
You need to #include <vector> and use the qualified name std::vector<int> x;:
#ifndef a_hh
#define a_hh
#include <vector>
class a{
public:
int i;
std::vector<int> x;
a() // or using initializer list: a() : i(0) {}
{
i=0;
}
};
#endif
Other points:
(as commented by EitanT) I removed the additional qualification a:: on the constructor
have a read of Why is "using namespace std" considered bad practice?
declaring a vector as a class member:
#include <iostream>
#include <vector>
using namespace std;
class class_object
{
public:
class_object() : vector_class_member() {};
void class_object::add_element(int a)
{
vector_class_member.push_back(a);
}
void class_object::get_element()
{
for(int x=0; x<vector_class_member.size(); x++)
{
cout<<vector_class_member[x]<<" \n";
};
cout<<" \n";
}
private:
vector<int> vector_class_member;
vector<int>::iterator Iter;
};
int main()
{
class_object class_object_instance;
class_object_instance.add_element(3);
class_object_instance.add_element(6);
class_object_instance.add_element(9);
class_object_instance.get_element();
return 0;
}
1.You need to #include <vector> and using namespace std, then a.hh just like below:
#ifndef a_hh
#define a_hh
#include <vector>
using namespace std;
class a
{
public:
int i;
vector <int> x;
a()
{
i = 0;
}
};
#endif
2. If you don't want to only use std namespace in all your code, you can specified the namespace before type, just like std::vector<int> x;