How to use clang libtooling to change specific ctor definition - libclang

I just want to rewrite specific ctor definition. To find witch on to change I use 'normal' DeclarationMatcher
DeclarationMatcher ClassMatcher2 =
cxxRecordDecl(isDerivedFrom("X"),
has(fieldDecl(
hasType(
cxxRecordDecl(
isDerivedFrom("AA")))))
).bind("className2");
It works great to find specific class/ctor but how I can switch from header to definition in cpp class. Command to run tool is:
loop-convert -extra-arg-before="-xc++" bin/playground/derived_t1.cpp --
To handle callback I use MatchCallback:
if (const CXXRecordDecl *FS = Result.Nodes.getNodeAs<clang::CXXRecordDecl>("className2")) {
llvm::outs() << "~~~~~~~~~~~~~~~~~~~BEGIN3\n";
FS->dump();
if(FS->hasUserDeclaredConstructor()) {
llvm::outs() << "FS->hasUserDeclaredConstructor() true\n";
}
if(FS->hasUserProvidedDefaultConstructor()) {
llvm::outs() << "FS->hasUserProvidedDefaultConstructor() true\n";
}
CXXRecordDecl::ctor_iterator bIt = FS->ctor_begin();
CXXRecordDecl::ctor_iterator eIt = FS->ctor_end();
llvm::outs() << "~~~~~~~~~~~~~~~~~~~BEGIN33\n";
while(bIt != eIt) {
bIt->dump();
++bIt;
}
llvm::outs() << "~~~~~~~~~~~~~~~~~~~~~END33\n";
m_rewrite.InsertText(FS->getLocStart(), "/* increment */", true, true);
llvm::outs() << "~~~~~~~~~~~~~~~~~~~~~END3\n";
}
As you already known from description m_rewriter.InsertText is not working for me - at all (no change in header has been made).
Input files derived_t1.h:
class AA {};
class AAA : public AA {};
class X {};
class Y : public X {}; // directly derived
class Z : public Y {}; // indirectly derived
typedef X A;
typedef A B;
class C : public B {}; // derived from a typedef of X
class BB : public X {
AAA aaa;
AAA aaa2;
int ttsd;
AA *as_ptr;
AA as;
public:
BB();
};
class CC : public X {
AAA * aaa_ptr;
};
class D {
AAA aaa;
AAA * aaa_ptr;
int g;
};
class E {
AAA aaa;
AAA * aaa_ptr;
int *gg_ptr;
};
Input files derived_t1.cpp:
#include "derived_t1.h"
BB::BB() {
//nothing for now here
}
Entire example tool:
#include "clang/Frontend/FrontendActions.h"
#include "clang/Tooling/CommonOptionsParser.h"
#include "clang/Tooling/Tooling.h"
#include "llvm/Support/CommandLine.h"
#include "clang/ASTMatchers/ASTMatchers.h"
#include "clang/ASTMatchers/ASTMatchFinder.h"
#include "clang/AST/ASTContext.h"
#include "clang/Basic/SourceManager.h"
#include "clang/Frontend/CompilerInstance.h"
#include "clang/Rewrite/Core/Rewriter.h"
//#include "clang/AST/RecursiveASTVisitor.h"
using namespace clang::tooling;
using namespace llvm;
using namespace clang;
using namespace clang::ast_matchers;
//static bool areSameVariable(const ValueDecl *First, const ValueDecl *Second) {
// return First && Second &&
// First->getCanonicalDecl() == Second->getCanonicalDecl();
//}
//static bool areSameExpr(ASTContext *Context, const Expr *First,
// const Expr *Second) {
// if (!First || !Second)
// return false;
// llvm::FoldingSetNodeID FirstID, SecondID;
// First->Profile(FirstID, *Context, true);
// Second->Profile(SecondID, *Context, true);
// return FirstID == SecondID;
//}
DeclarationMatcher ClassMatcher =
cxxRecordDecl(isDerivedFrom("X"
)).bind("className");
DeclarationMatcher MemberMatcher =
fieldDecl(
hasType(
cxxRecordDecl(
isDerivedFrom("AA"))
)).bind("field_decl");
DeclarationMatcher ClassMatcher2 =
cxxRecordDecl(isDerivedFrom("X"),
has(fieldDecl(
hasType(
cxxRecordDecl(
isDerivedFrom("AA")))))
).bind("className2");
class ClassPrinter : public MatchFinder::MatchCallback {
public:
ClassPrinter(Rewriter &rewriter):m_rewrite(rewriter) {
}
virtual void run(const MatchFinder::MatchResult &Result) {
if (const CXXRecordDecl *FS = Result.Nodes.getNodeAs<clang::CXXRecordDecl>("className")) {
llvm::outs() << "~~~~~~~~~~~~~~~~~~~BEGIN\n";
FS->dump();
llvm::outs() << "~~~~~~~~~~~~~~~~~~~~~END\n";
}
if (const CXXRecordDecl *F = Result.Nodes.getNodeAs<clang::CXXRecordDecl>("field_decl")) {
llvm::outs() << "~~~~~~~~~~~~~~~~~~~BEGIN2\n";
const auto& SM = *Result.SourceManager;
const auto& Loc = F->getLocation();
llvm::outs() << "Fname:"
<< SM.getFilename(Loc) << ":"
<< SM.getSpellingLineNumber(Loc) << ":"
<< SM.getSpellingColumnNumber(Loc) << "\n";
F->dump();
llvm::outs() << "~~~~~~~~~~~~~~~~~~~~~END2\n";
}
if (const CXXRecordDecl *FS = Result.Nodes.getNodeAs<clang::CXXRecordDecl>("className2")) {
llvm::outs() << "~~~~~~~~~~~~~~~~~~~BEGIN3\n";
FS->dump();
if(FS->hasUserDeclaredConstructor()) {
llvm::outs() << "FS->hasUserDeclaredConstructor() true\n";
}
if(FS->hasUserProvidedDefaultConstructor()) {
llvm::outs() << "FS->hasUserProvidedDefaultConstructor() true\n";
}
CXXRecordDecl::ctor_iterator bIt = FS->ctor_begin();
CXXRecordDecl::ctor_iterator eIt = FS->ctor_end();
llvm::outs() << "~~~~~~~~~~~~~~~~~~~BEGIN33\n";
while(bIt != eIt) {
//CXXConstructorDecl
bIt->dump();
++bIt;
}
llvm::outs() << "~~~~~~~~~~~~~~~~~~~~~END33\n";
m_rewrite.InsertText(FS->getLocStart(), "/* increment */", true, true);
llvm::outs() << "~~~~~~~~~~~~~~~~~~~~~END3\n";
}
if (const FieldDecl* fd = Result.Nodes.getNodeAs<clang::FieldDecl>("field_decl"))
{
llvm::outs() << "======== FieldDecl found ======\n";
const clang::RecordDecl* rd = fd->getParent();
const clang::QualType qt = fd->getType();
const clang::Type* t = qt.getTypePtr();
llvm::outs() << "FieldDecl found '"
<< fd->getQualifiedNameAsString() << " "
<< fd->getName().str() << "' in '"
<< rd->getName().str() << "'. "
<< "is Builtintype = " << t->isBuiltinType()
<< "\n";
if(Result.Nodes.getNodeAs<clang::CXXRecordDecl>("className")) {
llvm::outs() << "same AST as className\n";
}
else {
llvm::outs() << "NOT same AST as className\n";
}
}
if (const CXXConstructorDecl *FS = Result.Nodes.getNodeAs<clang::CXXConstructorDecl>("")) {
llvm::outs() << "~~~~~~~~~~~~~~~~~~~BEGIN4\n";
FS->dump();
llvm::outs() << "~~~~~~~~~~~~~~~~~~~~~END4\n";
}
}
private:
Rewriter &m_rewrite;
};
class IncrementForLoopHandler : public MatchFinder::MatchCallback {
public:
IncrementForLoopHandler(Rewriter &Rewrite) : Rewrite(Rewrite) {}
virtual void run(const MatchFinder::MatchResult &Result) {
const VarDecl *IncVar = Result.Nodes.getNodeAs<VarDecl>("incVarName");
Rewrite.InsertText(IncVar->getLocStart(), "/* increment */", true, true);
}
private:
Rewriter &Rewrite;
};//IncrementForLoopHandler
class MyASTConsumer : public ASTConsumer {
public:
MyASTConsumer(Rewriter &rewriter) : m_classPrinter(rewriter) {
Matcher.addMatcher(ClassMatcher, &m_classPrinter);
Matcher.addMatcher(MemberMatcher, &m_classPrinter);
Matcher.addMatcher(ClassMatcher2, &m_classPrinter);
}
void HandleTranslationUnit(ASTContext &Context) override {
// Run the matchers when we have the whole TU parsed.
llvm::outs() << "HANDLE TU:\n";
Matcher.matchAST(Context);
}
private:
ClassPrinter m_classPrinter;
MatchFinder Matcher;
};//MyASTConsumer
class MyFrontendAction : public ASTFrontendAction {
public:
MyFrontendAction() {}
void EndSourceFileAction() override {
llvm::outs() << "END OF FILE ACTION:\n";
m_theRewriter.getEditBuffer(m_theRewriter.getSourceMgr().getMainFileID())
.write(llvm::outs());
}
std::unique_ptr<ASTConsumer> CreateASTConsumer(CompilerInstance &CI, StringRef file) override {
m_theRewriter.setSourceMgr(CI.getSourceManager(), CI.getLangOpts());
return llvm::make_unique<MyASTConsumer>(m_theRewriter);
}
private:
Rewriter m_theRewriter;
};//MyFrontendAction
static llvm::cl::OptionCategory MyToolCategory("my-tool options");
static cl::extrahelp CommonHelp(CommonOptionsParser::HelpMessage);
// A help message for this specific tool can be added afterwards.
static cl::extrahelp MoreHelp("\nMore help text...\n");
int main(int argc, const char **argv) {
CommonOptionsParser OptionsParser(argc, argv, MyToolCategory);
ClangTool Tool(OptionsParser.getCompilations(),
OptionsParser.getSourcePathList());
return Tool.run(newFrontendActionFactory<MyFrontendAction>().get());
// return Tool.run(newFrontendActionFactory<clang::ASTPrintAction>().get());
}
So if someone have idea how to change that ctor in cpp file It will help me a lot. thx

Related

Begin was not declared in the scope for Generic Array Template Class

#include <iostream>
#include <string>
template <typename T, int N>
class Array {
int size{N};
T values{N};
friend std::ostream &operator<<(std::ostream &os, const Array<T, N> &arr) {
os << "[";
for (const auto &val: arr.values)
os << val << " ";
os << "]" << std::endl;
return os;
}
public:
Array() = default;
Array(T value_added) {
for(auto &value: values)
value = value_added;
}
void fill(T data_value) {
for (auto &value: values)
value = data_value;
}
int get_size() const {
return size;
}
T &operator[](int index) {
return values[index];
}
};
int main () {
Array<int,2> nums;
std::cout << "The size of nums is: " << nums.get_size() << std::endl;
std::cout << nums << std::endl;
}
I am trying to create a generic array template class but I get the error Begin was not declared in the scope and it says it is due to the for (const auto &val: arr.values) loop.

Spirit.X3 : using push_back_container traits with list parser

I have a class having public ctor and some add() method:
class object
{
object() {}
template <typename>
void add(T&& val) { // some adding here}
}
The main question I'm faced is how can I adopt spirit.x3 list parser to use object::add() method instead of std::vector<>::push_back ?
I was easily able to achieve what I need with simple
x3::int_ % ','
parser (live demo) using the following code :
#include <boost/spirit/home/x3.hpp>
#include <iostream>
#include <vector>
namespace x3 = boost::spirit::x3;
namespace parse_to_object
{
struct object
{
using value_type = int;
object() { std::cout << "object::object() - invoked" << std::endl; }
void add(value_type val) { _data.push_back(val); }
std::vector<value_type> _data;
};
const x3::rule<struct Test, object> r_ints("r_ints");
const auto r_ints_def = x3::int_ % ',';
BOOST_SPIRIT_DEFINE(r_ints);
}
namespace boost { namespace spirit { namespace x3 { namespace traits {
template<>
struct push_back_container<parse_to_object::object>
{
template<typename T>
static bool call(parse_to_object::object& obj, T&& val)
{
obj.add(std::move(val));
return true;
}
};
}}}}
int main()
{
const std::string text("1,2,3,4");
auto begin = std::begin(text);
const auto end = std::end(text);
parse_to_object::object result;
const auto ok = x3::phrase_parse(begin, end, parse_to_object::r_ints, x3::space, result);
std::cout << "ok = " << std::boolalpha << (ok && begin == end) << std::endl;
std::copy(result._data.begin(), result._data.end(), std::ostream_iterator<int>(std::cout, " "));
return 0;
}
But unfortunately, when I tried more compilcated example like
'{' >> x3::int_ >> ':' >> x3::int_ >> '}') % ','
I'm getting the compilation error (live demo) :
/opt/wandbox/boost-1.67.0/clang-head/include/boost/spirit/home/x3/support/traits/container_traits.hpp:102:45: error: no type named 'iterator' in 'parse_to_object::object'
: mpl::identity {};
Could somebody assist with spirit.x3 traits and give some example how to abopt custom class to be used instead of std::vector<> for list parser ?
In the end it's down to a missing include:
#include <boost/fusion/adapted/std_pair.hpp>
std::pair isn't adapted by default.
Side note: std::move should be std::forward<T> with "universal references" (or perfect forwarding)
Live On Coliru
#define BOOST_SPIRIT_X3_DEBUG
#include <boost/fusion/adapted/std_pair.hpp>
#include <boost/spirit/home/x3.hpp>
#include <iostream>
#include <vector>
namespace x3 = boost::spirit::x3;
namespace parse_to_object
{
struct object
{
using value_type = std::pair<int,int>;
object() { std::cout << "object::object() - invoked" << std::endl; }
void add(value_type val) { _data.push_back(std::move(val)); }
std::vector<std::pair<int,int>> _data;
};
const x3::rule<struct Test, object> r_ints("r_ints");
const auto r_ints_def = ('{' >> x3::int_ >> ':' >> x3::int_ >> '}') % ',';
BOOST_SPIRIT_DEFINE(r_ints)
}
namespace boost { namespace spirit { namespace x3 { namespace traits {
template<> struct push_back_container<parse_to_object::object>
{
template<typename T>
static bool call(parse_to_object::object& obj, T&& val)
{
obj.add(std::forward<T>(val));
return true;
}
};
}}}}
int main()
{
const std::string text("{1:2},{3:4}");
auto begin = std::begin(text), end = std::end(text);
parse_to_object::object result;
auto ok = phrase_parse(begin, end, parse_to_object::r_ints >> x3::eoi, x3::space, result);
std::cout << "ok = " << std::boolalpha << ok << "\n";
for (auto& p : result._data)
std::cout << "{" << p.first << ", " << p.second << "} ";
std::cout << "\n";
}
Prints
object::object() - invoked
<r_ints>
<try>{1:2},{3:4}</try>
<success></success>
<attributes></attributes>
</r_ints>
ok = true
{1, 2} {3, 4}

boost phoenix bind a semantic action with multiple parameter and a return value

I'm new to both C++ and Boost spirit.
I'm stuck on this for a day now.
I want to parse two strings separated by a dot.
basically, I need following strings to be parsed to an integer.
eg: [field] --> integer // working
eg2: [instance.field] --> integer // not working
For the 2nd one, I need to take two strings as parameters and evaluate them and return the relevant integer value.
I must have missed a basic thing, but I can't figure it out.
Please let me know the error in my code or a better way to do it.
calling a method and getting the value is needed. I can't change that.
this is the code peace.
#define BOOST_SPIRIT_USE_PHOENIX_V3
#include <boost/config/warning_disable.hpp>
#include <boost/spirit/include/qi.hpp>
#include <boost/spirit/include/phoenix.hpp>
#include <boost/spirit/include/support_utree.hpp>
#include <iostream>
#include <string>
namespace qi = boost::spirit::qi;
namespace ascii = boost::spirit::ascii;
namespace spirit = boost::spirit;
namespace phx = boost::phoenix;
int fieldVal(std::vector<char> fieldName) {
std::string fieldValue(fieldName.begin(), fieldName.end());
std::cout << "Field Name Recieved.::: " << fieldValue << std::endl;
int i = 50; //just for test
return i;
}
int instanceFieldVal(std::string instance, std::string fieldName) {
std::cout << "Recieved ::: " << instance <<" : "<< fieldName << std::endl;
int i = 60; //just for test
return i;
}
namespace client
{
template <typename Iterator>
struct calculator : qi::grammar<Iterator, ascii::space_type, int()>
{
calculator() : calculator::base_type(instanceFieldValue)
/*change base type to "field" and comment everything relevant to
"instanceFieldValue", then it's working */
{
using qi::int_;
using qi::_val;
using qi::_1;
using qi::char_;
using qi::lexeme;
field = lexeme[
'['
>> +(~char_(".]["))
>> ']'
][qi::_val = phx::bind(&fieldVal, qi::_1)]; // this is working
instanceField = '['
>> +(~char_(".]["))
>> '.'
>> +(~char_(".]["))
>> ']';
instanceFieldValue
= instanceField[qi::_val = phx::bind(&instanceFieldVal, qi::_1)];
// how ^this line should be changed??
}
qi::rule<Iterator, ascii::space_type, int()> field, instanceFieldValue;
qi::rule<Iterator, ascii::space_type, std::string(), std::string()>instanceField;
};
}
int main()
{
std::cout << "Type an expression...or [q or Q] to quit\n\n";
using boost::spirit::ascii::space;
using boost::spirit::utree;
typedef std::string::const_iterator iterator_type;
typedef client::calculator<iterator_type> calculator;
calculator calc; // Our grammar
std::string str;
while (std::getline(std::cin, str))
{
if (str.empty() || str[0] == 'q' || str[0] == 'Q')
break;
std::string::const_iterator iter = str.begin();
std::string::const_iterator end = str.end();
int val;
bool r = phrase_parse(iter, end, calc, space, val);
if (r && iter == end)
{
std::cout << "-------------------------\n";
std::cout << "Parsing succeeded: " << val << "\n";
std::cout << "-------------------------\n";
}
else
{
std::string rest(iter, end);
std::cout << "-------------------------\n";
std::cout << "Parsing failed\n";
std::cout << "stopped at: \": " << rest << "\"\n";
std::cout << "-------------------------\n";
}
}
std::cout << "Bye... :-) \n\n";
return 0;
}
Well, you say it's a function with multiple arguments, but then you only pass one arg: _1.
How can that work? It can't. It's also completely what you even want to pass, because the second parameter is likely from the instanceField expression, but the first is ... magical context.
Like always, I'd try to minimize state and semantic actions. In fact, I'd suggest separation of concerns:
parse to std:vector<std::string> first
on successful parse, evaluate it
That would lead to a grammar like
template <typename Iterator>
struct path : qi::grammar<Iterator, std::deque<std::string>()> {
path() : path::base_type(start) {
using namespace qi;
name = +(graph - char_(".][")); // not matching spaces please
qualifiedName = name % '.';
start = skip(ascii::space) ['[' >> qualifiedName >> ']'];
BOOST_SPIRIT_DEBUG_NODES((start)(qualifiedName)(name))
}
private:
qi::rule<Iterator, std::deque<std::string>(), qi::ascii::space_type> qualifiedName;
qi::rule<Iterator, std::string()> name;
qi::rule<Iterator, std::deque<std::string>()> start;
};
You can of course combine it using a semantic action if you insist:
template <typename Iterator>
struct property : qi::grammar<Iterator, int()> {
property() : property::base_type(start) {
using namespace qi;
name = +(graph - char_(".][")); // not matching spaces please
qualifiedName = name % '.';
start = skip(ascii::space) ['[' >> qualifiedName >> ']']
[_val = phx::bind(lookup, phx::cref(sample), _1) ]
;
BOOST_SPIRIT_DEBUG_NODES((start)(qualifiedName)(name))
}
private:
qi::rule<Iterator, std::deque<std::string>(), qi::ascii::space_type> qualifiedName;
qi::rule<Iterator, std::string()> name;
qi::rule<Iterator, int()> start;
};
Now, just implement a function
int lookup(Node const& context, std::deque<std::string> path);
As an example, let's imagine a sample context:
using Leaf = int;
using Node = boost::make_recursive_variant< Leaf, std::map<std::string, boost::recursive_variant_> >::type;
using SubTree = std::map<std::string, Node>;
static Node sample = SubTree {
{ "simple", 100 },
{ "compound", SubTree {
{ "first", 200 },
{ "second", 300 },
}, },
{ "deep", SubTree {
{ "nested", SubTree {
{ "compound", SubTree {
{ "buried", 400 },
{ "secrets", 500 },
}, }, }, }, }, }
};
Now, an implementation of lookup could be as simple as:
int lookup(Node const& context, std::deque<std::string> path) {
if (path.empty())
return boost::get<Leaf>(context);
auto& sub = boost::get<SubTree>(context);
auto element = path.front();
path.pop_front();
try {
return lookup(sub.at(element), std::move(path));
} catch(std::out_of_range const& e) {
throw std::runtime_error("'" + element + "' not found");
}
}
Full Demo
Live On Coliru
//#define BOOST_SPIRIT_DEBUG
#include <boost/spirit/include/qi.hpp>
#include <boost/spirit/include/phoenix.hpp>
#include <iostream>
#include <map>
namespace qi = boost::spirit::qi;
namespace phx = boost::phoenix;
using Leaf = int;
using Node = boost::make_recursive_variant< Leaf, std::map<std::string, boost::recursive_variant_> >::type;
using SubTree = std::map<std::string, Node>;
static Node sample = SubTree {
{ "simple", 100 },
{ "compound", SubTree {
{ "first", 200 },
{ "second", 300 },
}, },
{ "deep", SubTree {
{ "nested", SubTree {
{ "compound", SubTree {
{ "buried", 400 },
{ "secrets", 500 },
},
},
},
},
},
}
};
int lookup(Node const& context, std::deque<std::string> path) {
if (path.empty())
return boost::get<Leaf>(context);
auto& sub = boost::get<SubTree>(context);
auto element = path.front();
path.pop_front();
try {
return lookup(sub.at(element), std::move(path));
} catch(std::out_of_range const& e) {
throw std::runtime_error("'" + element + "' not found");
}
}
namespace parser {
template <typename Iterator>
struct property : qi::grammar<Iterator, int()> {
property() : property::base_type(start) {
using namespace qi;
name = +(graph - char_(".][")); // not matching spaces please
qualifiedName = name % '.';
start = skip(ascii::space) ['[' >> qualifiedName >> ']']
[_val = phx::bind(lookup, phx::cref(sample), _1) ]
;
BOOST_SPIRIT_DEBUG_NODES((start)(qualifiedName)(name))
}
private:
qi::rule<Iterator, std::deque<std::string>(), qi::ascii::space_type> qualifiedName;
qi::rule<Iterator, std::string()> name;
qi::rule<Iterator, int()> start;
};
}
int main() {
using It = std::string::const_iterator;
parser::property<It> calc;
for (std::string const str : {
"[simple]",
"[compound.first]",
"[compound.second]",
"[deep.nested.compound.buried]",
"[deep.nested.compound.secrets]",
// whitespace is ok
" [ compound.\tfirst ]",
// failing:
"[]",
"[missing]",
"[deep.missing.compound.buried]",
// whitespace not ok inside names
" [ compound.\tfi rst ]",
})
try {
std::cout << " ===== Input: '" << str << "'\n";
It iter = str.begin(), end = str.end();
int val;
bool r = parse(iter, end, calc, val);
if (r) {
std::cout << "Parsing succeeded: " << val << "\n";
} else {
std::cout << "Parsing failed\n";
}
if (iter != end) {
std::cout << " - Remaining unparsed input: '" << std::string(iter, end) << "'\n";
}
} catch(std::exception const& e) {
std::cout << "Exception: " << e.what() << "\n";
}
}
Prints:
===== Input: '[simple]'
Parsing succeeded: 100
===== Input: '[compound.first]'
Parsing succeeded: 200
===== Input: '[compound.second]'
Parsing succeeded: 300
===== Input: '[deep.nested.compound.buried]'
Parsing succeeded: 400
===== Input: '[deep.nested.compound.secrets]'
Parsing succeeded: 500
===== Input: ' [ compound. first ]'
Parsing succeeded: 200
===== Input: '[]'
Parsing failed
- Remaining unparsed input: '[]'
===== Input: '[missing]'
Exception: 'missing' not found
===== Input: '[deep.missing.compound.buried]'
Exception: 'missing' not found
===== Input: ' [ compound. fi rst ]'
Parsing failed
- Remaining unparsed input: ' [ compound. fi rst ]'

How to keep all properties of graph read by boost::read_graphviz?

Suppose you want to read in a .dot graph into boost where there could be properties you do not recognize. It is easy to ignore them (by passing ignore_other_properties to the dynamic_properties constructor) but what if you wanted all the properties added to dynamic_properties instead?
The below code demonstrates the problem. The handle_custom_properties is a copy of ignore_other_properties and the code will compile / run, reporting "3 vertices, 2 edges." What needs to be added to handle_custom_properties so that, on return, dp will contain a property "label" and node A will have value "x" for the label property?
#include <iostream>
#include <string>
#include <boost/graph/graphviz.hpp>
#include <boost/graph/adjacency_list.hpp>
#include <boost/exception/exception.hpp>
#include <boost/exception/diagnostic_information.hpp>
struct vertex_p {
std::string node_id;
};
typedef boost::adjacency_list < boost::vecS, boost::vecS, boost::undirectedS, vertex_p> graph_t;
boost::shared_ptr<boost::dynamic_property_map>
handle_custom_properties(const std::string& s,
const boost::any& k,
const boost::any& v) {
// What goes in here to add dynamic property map for property "s" which key-value pair <k,v>?
// What ignore_other_properties does
return boost::shared_ptr<boost::dynamic_property_map>();
}
int main() {
std::string str(R"(graph {
A [ label="x" ]
B
C
A -- B
A -- C
}
)");
try {
graph_t g;
boost::dynamic_properties dp{handle_custom_properties};
dp.property("node_id", get(&vertex_p::node_id, g));
if (boost::read_graphviz(str, g, dp)) {
std::cout << "read_graphviz returned success" << std::endl;
std::cout << "graph stats:" << std::endl;
std::cout << " " << g.m_vertices.size() << " vertices" << std::endl;
std::cout << " " << g.m_edges.size() << " edges" << std::endl;
}
else {
std::cout << "read_graphviz returned failure" << std::endl;
}
}
catch (std::exception& e) {
std::cerr << e.what() << std::endl;
}
catch (boost::exception& e) {
std::cerr << boost::diagnostic_information(e) << std::endl;
}
}
I was not able to find an existing class to solve this but implementing a dynamic_property_map in terms of a std::map worked:
template<typename TKey, typename TValue>
class dynamic_property_map_impl : public boost::dynamic_property_map {
std::map<TKey, TValue> map_;
public:
boost::any get(const boost::any& key) override { return map_[boost::any_cast<TKey>(key)]; }
std::string get_string(const boost::any& key) override { std::ostringstream s; s << map_[boost::any_cast<TKey>(key)]; return s.str(); }
void put(const boost::any& key, const boost::any& value) override { map_[boost::any_cast<TKey>(key)] = boost::any_cast<TValue>(value); }
const std::type_info& key() const override { return typeid(TKey); }
const std::type_info& value() const override { return typeid(TValue); }
};
boost::shared_ptr<boost::dynamic_property_map>
handle_custom_properties(const std::string&,
const boost::any&,
const boost::any&) {
return boost::make_shared<dynamic_property_map_impl<unsigned, std::string>>();
}
Changing the graph and printing out the properties shows all the property maps were added:
std::string str(R"(graph {
A [ label="x", stuff="y" ]
B
C [ happy="yes" ]
A -- B
A -- C
}
)");
...
std::cout << "properties:" << std::endl;
for (const auto& p : dp) {
std::cout << " " << p.first << std::endl;
}
Outputs
read_graphviz returned success
graph stats:
3 vertices
2 edges
properties:
happy
label
node_id
stuff

How to get error object? when use member function in deadline_timer

I use boost::asio::deadline_timer using a member function as a handler (callback function).
If I cancel a timer, how to get error object in print() member function?
class printer
{
public:
printer(boost::asio::io_service& io)
: timer_(io, boost::posix_time::seconds(1)),
count_(0)
{
timer_.async_wait(boost::bind(&printer::print, this));
}
~printer()
{
std::cout << "Final count is " << count_ << "\n";
}
void print()
{
if (count_ < 5)
{
std::cout << count_ << "\n";
++count_;
timer_.expires_at(timer_.expires_at() + boost::posix_time::seconds(1));
timer_.async_wait(boost::bind(&printer::print, this));
}
}
private:
boost::asio::deadline_timer timer_;
int count_;
};
int main()
{
boost::asio::io_service io;
printer p(io);
io.run();
return 0;
}
I try to set error object using bind in async_wait(), but it's compile error
timer_.async_wait(boost::bind(&printer::print, this, boost::asio::placeholders::error));
As long as your method signature matches, it should be no problem:
void print(boost::system::error_code const ec)
// and
boost::bind(&printer::print, this, boost::asio::placeholders::error)
See it Live On Coliru:
#include <boost/asio.hpp>
#include <boost/bind.hpp>
#include <iostream>
class printer
{
public:
printer(boost::asio::io_service& io)
: timer_(io, boost::posix_time::seconds(1)),
count_(0)
{
timer_.async_wait(boost::bind(&printer::print, this, boost::asio::placeholders::error));
}
~printer()
{
std::cout << "Final count is " << count_ << "\n";
}
void print(boost::system::error_code const ec)
{
if (ec)
std::cout << "Error: " << ec.message() << "\n";
if (count_ < 5)
{
std::cout << count_ << "\n";
++count_;
timer_.expires_at(timer_.expires_at() + boost::posix_time::seconds(1));
timer_.async_wait(boost::bind(&printer::print, this, boost::asio::placeholders::error));
}
}
private:
boost::asio::deadline_timer timer_;
int count_;
};
int main()
{
boost::asio::io_service io;
printer p(io);
io.run();
}

Resources