Why does my current_user pointer reset? - function

current_user pointer works within functions of System.cpp, but resets afterwards. The current_user seems to be updated locally when it's set within the function createUser(string name) when it's called in the switch statement. However, when I try to call it outside the function it seems to have not been updated at all. Not entirely sure what's going on.
Main.cpp
#include <iostream>
#include "System.h"
#include "User.h"
using namespace std;
int main()
{
System s;
s.run();
// Works
User a("Test");
User b("Test 2");
User c("Test 3");
User* current = &a;
cout << "The current user is: " << current->getName() << endl;
current = &b;
cout << "Now it's: " << current->getName() << endl;
current = &c;
cout << "The final current user is: " << current->getName() << endl;
// Does not work
cout << "Current user: " << s.getCurrentUser()->getName() << endl;
}
System.h
#ifndef SYSTEM_H
#define SYSTEM_H
#include "User.h"
#include "Group.h"
#include "MessageBuffer.h"
#include "Banner.h"
#include <iostream>
#include <vector>
class System
{
public:
System();
char validInput(std::string inputIn);
bool validUsername(std::string nameIn);
bool userExists(std::string nameIn);
void createUser(std::string nameIn);
void run();
User* getCurrentUser();
private:
User* current_user;
std::vector<User> user_list;
std::vector<Group> group_list;
};
#endif // SYSTEM_H
System.cpp
// Program 1: TigerBook Social Network
// File: System.cpp
// Description: Class Implimentation of the System Class. Instantiates objects that must be initialized and handles
// basic user screw-ups (choosing and option out of bounds).
#include <iostream>
#include "System.h"
using namespace std;
// Function: Default System Constructor
// Inputs: None
// Description: Default constructor for the class.
System::System()
{
}
User* System::getCurrentUser()
{
return current_user;
}
// Function: validInput
// Inputs: string inputIn
// Outputs: char value of input at 0
// Description: Determines whether the input is valid.
char System::validInput(string inputIn)
{
if (inputIn.length() == 1)
{
return inputIn[0];
}
else
{
return '0';
}
}
// Function: validUsername
// Inputs: string username
// Outputs: true if valid, false if not
// Description: Determines whether the username is valid
bool System::validUsername(string nameIn)
{
if (nameIn.empty() || nameIn.length() < 2 || (nameIn.find_first_not_of("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ") != string::npos))
{
cerr << "\n\t*** ERROR: Invalid user name, please try again! ***" << endl;
return false;
}
else
{
return true;
}
}
// Function: userExists
// Inputs: string username
// Outputs: true if exists, false if not
// Description: Determines whether the username exists in user_list.
bool System::userExists(string nameIn)
{
return false;
}
// Function: createUser
// Inputs: string username
// Outputs: void
// Description: Creates new user and adds it to user_list.
void System::createUser(string nameIn)
{
User u(nameIn);
user_list.push_back(u);
current_user = &u;
}
// Function: run
// Inputs: None
// Outputs: void
// Description: Program driver, handles basic user input and screw-ups
void System::run()
{
//current_user = NULL;
Banner banner("The TigerBook Social Network!");
cout << banner.getBanner() << endl;
bool quit = false;
string input;
while(!quit)
{
cout << "\nCreate new user (n), Broadcast (b), Multicast (m), Unicast (u), Wall page (w), Home page (h), Create new group (g), " << endl;
cout << "Join a group (j), Switch user (s), Quit (q)\n" << endl;
cout << "Choose an option: ";
getline(cin, input);
if (current_user == NULL && (input != "N" && input != "n" && input != "Q" && input != "q"))
{
cerr << "\n\t*** ERROR: There is no current user, please create a new user! ***" << endl;
continue;
}
switch (validInput(input))
{
// Create new user
case 'N':
case 'n':
{
string username;
cout << "\nPlease enter user name: ";
getline(cin, username);
if (!validUsername(username))
{
continue;
}
else if (userExists(username))
{
cerr << "\n\t*** ERROR: The user \"" + username + "\" already exists, please try again! ***" << endl;
continue;
}
else
{
createUser(username);
cout << "\nCurrent user: " << getCurrentUser()->getName() << endl; // test current_user
}
break;
}
case 'B':
case 'b':
{
break;
}
case 'M':
case 'm':
{
break;
}
case 'U':
case 'u':
{
break;
}
case 'W':
case 'w':
{
break;
}
case 'H':
case 'h':
{
break;
}
case 'G':
case 'g':
{
break;
}
case 'J':
case 'j':
{
break;
}
case 'S':
case 's':
{
break;
}
case 'Q':
case 'q':
{
quit = true;
banner.setBanner("Thank you for using TigerBook Social Network!");
cout << banner.getBanner() << endl << endl;
break;
}
default:
{
cerr << "\n\t*** ERROR: Invalid input, please try again! ***" << endl;
}
} // End of switch statement
} // End of loop
}
User.h
#ifndef USER_H
#define USER_H
#include <string>
class User
{
public:
User();
User(std::string nameIn);
std::string getName();
void setName(std::string nameIn);
private:
std::string name;
};
#endif // USER_H
User.cpp
// Program 1: TigerBook Social Network
// File: User.cpp
// Description: Class implementation for User class
#include "User.h"
using namespace std;
// Constructor (Default)
User::User()
{
//ctor
}
// Constructor
// Inputs: string that sets name
// Description: Constructs user object and assigns its name.
User::User(string nameIn)
{
name = nameIn;
}
// Function: setName
// Inputs: string that sets name
// Outputs: void
// Description: Sets name of user object.
void User::setName(string nameIn)
{
name = nameIn;
}
// Function: getName
// Inputs: none
// Outputs: Name of user
// Description: Returns the name of the user object.
string User::getName()
{
return name;
}

You have:
void System::createUser(string nameIn)
{
User u(nameIn);
user_list.push_back(u);
current_user = &u;
}
Here, you are storing pointer to a local variable. The pointer becomes a dangling pointer as soon as the function returns. Any use of current_usr to access the object after the function returns is cause for undefined behavior.
You can use:
void System::createUser(string nameIn)
{
User u(nameIn);
user_list.push_back(u);
current_user = &(user_list.back());
}
Even that is very fragile. It will work if you carefully manage the objects in user_list.
A better option will be not to store a pointer to the object at all. You can, for example, use:
User* System::getCurrentUser()
{
if ( user_list.empty() )
{
return nullptr;
}
return &(user_list.back());
}

Related

FromBluetoothAddressAsync function return null

Sometimes FromBluetoothAddressAsync() from WinRT returns NULL:
BluetoothLEDevice::FromBluetoothAddressAsync(deviceAddr).get();
Thats depends by deviceAddr, thats mean that function for some of remote devices everytime returns NULL, for some of devices return BluetoothLEDevice object.
Somewhere in the Internet, I found a mention that this is a Microsoft problem and nothing can be done about it, but that was a few years ago. Therefore, the question to those who often use this function is: what kind of devices cannot receive BluetoothLEDevice object and how to proceed in this case?
GetFirstAdvertisingBLEAddr():
uint64_t GetFirstAdvertisingBLEAddr()
{
std::mutex m;
std::condition_variable cv;
BluetoothLEAdvertisementWatcher advWatcher;
uint64_t addr = 0;
bool stopCalled = false;
auto recvToken = advWatcher.Received([&addr, &m, &cv](BluetoothLEAdvertisementWatcher watcher, BluetoothLEAdvertisementReceivedEventArgs eventArgs) {
ShowAdvertisement(eventArgs);
addr = eventArgs.BluetoothAddress();
watcher.Stop();
std::unique_lock l(m);
cv.notify_all();
});
auto stoppedToken = advWatcher.Stopped([&stopCalled, &m, &cv](BluetoothLEAdvertisementWatcher watcher, BluetoothLEAdvertisementWatcherStoppedEventArgs eventArgs) {
std::unique_lock l(m);
stopCalled = true;
cv.notify_all();
});
{
std::unique_lock l(m);
advWatcher.Start();
cv.wait(l, [&addr, &stopCalled, &advWatcher] { return addr != 0 && stopCalled && (advWatcher.Status() == BluetoothLEAdvertisementWatcherStatus::Aborted || advWatcher.Status() == BluetoothLEAdvertisementWatcherStatus::Stopped); });
}
// remove event handlers
advWatcher.Received(recvToken);
advWatcher.Stopped(stoppedToken);
return addr;
}
GetDeviceName():
std::wstring GetDeviceName(uint64_t deviceAddr) {
BluetoothLEDevice dev = BluetoothLEDevice::FromBluetoothAddressAsync(deviceAddr).get();
if (NULL == dev) {
std::wstringstream resp;
resp << L"NULL" << AddrToString(deviceAddr);
return resp.str();
}
auto gapServicesResult = dev.GetGattServicesForUuidAsync(GattServiceUuids::GenericAccess(), BluetoothCacheMode::Uncached).get();
if (gapServicesResult.Status() == GattCommunicationStatus::Success) {
auto gapServices = gapServicesResult.Services();
if (gapServices.Size() > 0) {
GattDeviceService genericAccessSvc = gapServices.GetAt(0);
if (genericAccessSvc) {
auto gapDeviceNameChrs = genericAccessSvc.GetCharacteristics(GattCharacteristicUuids::GapDeviceName());
if (gapDeviceNameChrs.Size() == 1) {
GattCharacteristic gapDeviceNameChr = gapDeviceNameChrs.GetAt(0);
GattReadResult readRes = gapDeviceNameChr.ReadValueAsync().get();
if (readRes.Status() == GattCommunicationStatus::Success) {
DataReader reader = DataReader::FromBuffer(readRes.Value());
return reader.ReadString(reader.UnconsumedBufferLength()).c_str();
}
}
}
}
}
main():
int main()
{
winrt::init_apartment();
std::wcout << "Main thread: " << std::this_thread::get_id() << std::endl;
uint64_t addr = GetFirstAdvertisingBLEAddr();
if (addr == 0) {
std::wcout << L"Failed to find advertising BLE device." << std::endl;
return 1;
}
std::wcout << "Found BLE device: " << GetDeviceName(addr) << std::endl;
BluetoothLEDevice dev = BluetoothLEDevice::FromBluetoothAddressAsync(addr).get();
GattDeviceServicesResult result = dev.GetGattServicesAsync(BluetoothCacheMode::Uncached).get();
.......

C program giving weird output from HTML GET input and prints at bottom of HTML page

I made a C webserver that is able to display an HTML page and then take input from a text box. So far it works to display basic commands like ls but for some reason when i enter into the text box the command ls -l | wc -l it will only act as if i typed in ls -l | w and wont process the command because of this. Based on how im tokenizing i dont understand why it wont go past this line? In the textbox that ends up being the input it comes as ls+-l+%7C+wc+-l which is why i had to separate it in this way. Also my output displays at the bottom of the html page, how can i place it in the correct output areas?
Server.c
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#include <arpa/inet.h>
#include <sys/wait.h>
#include <signal.h>
#include <poll.h>
#define PORT "3838" //port being connected to
#define MAXLEN 800
#define BACKLOG 10 //number of pending connections to be held in queue
//format of html page
char header []=
"HTTP/1.1 200 Ok\r\n"
"Content-Type: text/html; charset=UTF-8\r\n\r\n"
"<!DOCTYPE html>\r\n"
"<html>\n"
"<head>\n"
"<title>Web-Based Remote Command Server</title>\r\n"
"</head>\n"
"<body>\n\n";
char input []=
"<form action= \"/run\" method= \"GET\"> \n"
"Command: <input type=\"text\" size=\"100\" name=\"command\">\n"
"<input type=\"submit\" value=\"Run\">\n"
"</form>";
char output []=
"<p>Command that was run and testing this:</p>\n"
"<pre>Your server will include the command that was just run here.</pre>\n\n"
"<p>Standard Output:</p>\n""<pre>Your server will include the stdout results here.</pre>\n\n";
char outputerr[]=
"<p>Standard Error:</p>\n"
"<pre>Your server will include the stderr results here.</pre>\r\n\r\n"
"</body>\r\n""</html>\r\n";
char *buff = header;
void sigchld_handler(int s)
{
(void)s; // quiet unused variable warning
// waitpid() might overwrite errno, so we save and restore it:
int saved_errno = errno;
while(waitpid(-1, NULL, WNOHANG) > 0);
errno = saved_errno;
}
void *get_in_addr(struct sockaddr *sa)
{
if (sa->sa_family == AF_INET) {
return &(((struct sockaddr_in*)sa)->sin_addr);
}
return &(((struct sockaddr_in6*)sa)->sin6_addr);
}
char *parse(char *command){
char * newCommand = (char *) malloc(sizeof(char)*50);
int tgt = 0;
newCommand = strtok(command, " ");
//printf("%s\n", newCommand);
newCommand = strtok(NULL, "/run?command= ");
//printf("%s\n", newCommand);
for(int src = 0; src< strlen(newCommand); src++){
if(newCommand[src] == '+')
{
newCommand[src] = ' ';
}
else if(newCommand[src] == '%')
{
newCommand[src] = ' ';
}
else if(newCommand[src] == '7')
{
newCommand[src] = ' ';
}
else if(newCommand[src] == 'C')
{
newCommand[src] = '|';
}
}
return newCommand;
}
char * execution(char *command){
//printf("yo:%s\n",command );
int piper[2];
size_t len = 0;
pipe(piper);
char* output = malloc(1000 * sizeof(char));
memset(output, '\0', 1000* sizeof(char));
pid_t pid = fork();
if(pid != 0)// parent
{
wait(NULL);
close(piper[1]);
int n = sizeof(output);
struct pollfd * poll_fd = malloc(sizeof(struct pollfd));
poll_fd->fd = piper[0];
poll_fd->events = POLLIN;
//wait(NULL);
//printf("done\n");
//printf("AAA %s", output);
if(poll(poll_fd, 1, 0) == 1){ // pipe data check
read(piper[0], output, 1000);
}
//printf("the command is %s\n", output);
//read(&output,output, piper[0]);
// printf("%s\n",piper[0]);
// dup2(piper[1],1);
// close(0)
}
else{
//dup2(piper[1], 1);
//printf("run : %s", command);
close(1);
dup(piper[1]);
//close(0);
execlp(command, command, NULL);
exit(1);
}
// dup2 execute and print it out in parent
// if(*(commands+1)!=NULL) // redirect stdout as long as were not at the last row
// {
// dup2(piper[1],1);
// close(piper[0]);
// }
return output;
}
int main (void){
int sockfd;
int new_fd;
struct addrinfo hints;
struct addrinfo *serverinfo;
struct addrinfo *p;
struct sockaddr_storage client_addr;
socklen_t addrsize;
struct sigaction sa;
int yes = 1;
char s[INET6_ADDRSTRLEN];
int status;
memset(&hints, 0, sizeof hints); //makes struct empty
hints.ai_family = AF_UNSPEC; //IPv4 or v6
hints.ai_socktype = SOCK_STREAM; //TCP type need
hints.ai_flags = AI_PASSIVE; //Fill in IP for us
//if can't get address info print error
if((status = getaddrinfo(NULL, PORT, &hints, &serverinfo)) != 0){
fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(status));
return 1;
}
for(p = serverinfo; p != NULL; p = p->ai_next){
if((sockfd = socket(p->ai_family, p->ai_socktype, p->ai_protocol)) == -1){
perror("server: socket");
continue;
}
if(setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(int)) == -1){
perror("setsockopt");
exit(1);
}
if(bind(sockfd, p->ai_addr, p->ai_addrlen) == -1){
close(sockfd);
perror("server: bind");
continue;
}
break;
}
freeaddrinfo(serverinfo);
if(p == NULL){
fprintf(stderr, "server: failed to bind\n");
exit(1);
}
if(listen(sockfd, BACKLOG) == -1){
perror("listen");
exit(1);
}
sa.sa_handler = sigchld_handler; // reap all dead processes
sigemptyset(&sa.sa_mask);
sa.sa_flags = SA_RESTART;
if (sigaction(SIGCHLD, &sa, NULL) == -1) {
perror("sigaction");
exit(1);
}
printf("server: waiting for connections....\n");
while(1){
addrsize = sizeof client_addr;
new_fd = accept(sockfd, (struct sockaddr *)&client_addr, &addrsize);
if(new_fd == -1){
perror("Did not accept");
continue;
}
inet_ntop(client_addr.ss_family, get_in_addr((struct sockaddr *)&client_addr), s, sizeof s);
printf("server: got connection from %s\n", s);
if(!fork()){
close(sockfd);
int bufsize = 1024;
char *buffer = malloc(bufsize);
recv(new_fd, buffer, bufsize, 0);
send(new_fd, header, bufsize, 0);
//printf("%s\n", buffer);
//printf("%s\n", parse(buffer));
//printf("%s\n", execution(parse(buffer)));
//int length = strlen(output);
//output [length + 1] = execution(parse(buffer));
//write(new_fd, "HTTP/1.1 200 OK\n", 16);
//write(new_fd, "Content-length: 46\n", 19);
//write(new_fd, "Content-type: text/html\n\n", 25);
//write(new_fd, "<html><head>\n<head>\n<title>The CAvengers Web Page</title>\n</head>\n</html>", 46);
if(send(new_fd, execution(parse(buffer)), 1000, 0) == -1)
perror("send");
close(new_fd);
exit(0);
}
close(new_fd);
}
return 0;
}

Exception throw in boost::asio::spawn not caught by try catch

In this convoluted example, two for loops are started by boost::asio::spawn() asynchronously. The first for loop prints an odd number every 1000us and the second one prints an even number every 1000us.
I expect the output to be something like 1 2 3 4 5 6 and then the 'Throw an error' message should be printed to stderr by the call to cerr.
However, the exception is actually thrown in loop.run() so it is not caught by the try catch block.
Can someone point out how to properly catch the runtime_error when calling spawn()? Does spawn() not reraise the exception thrown in the spawned coroutine to its parent scope?
using namespace std;
using namespace boost::asio;
using boost::posix_time::microseconds;
io_service loop;
spawn(loop, [&loop](yield_context yield)
{
try
{
spawn(loop, [&loop](yield_context yield)
{
deadline_timer timer{loop};
for(unsigned i = 0; i < 3; ++i)
{
cout << i * 2 + 1 << endl;
timer.expires_from_now(microseconds(1000));
timer.async_wait(yield);
}
throw runtime_error("Throw an error");
});
spawn(loop, [&loop](yield_context yield)
{
deadline_timer timer{loop};
for(unsigned i = 0; i < 3; ++i)
{
cout << (i + 1) * 2 << endl;
timer.expires_from_now(microseconds(1000));
timer.async_wait(yield);
}
});
} catch(const runtime_error& ex)
{
cerr << ex.what() << endl;
}
});
loop.run();
All handlers are invoked by the service loop, meaning you always need to handle errors: Should the exception thrown by boost::asio::io_service::run() be caught?
Sidenote: In the case of coroutines, it's my experience that catching by reference is a bit tricky. This more than likely has to do with the lifetime of the coroutine stack itself.
You can demonstrate it using:
while (true) {
try {
loop.run();
break;
} catch(std::runtime_error ex) {
std::cerr << "L:" << __LINE__ << ": " << ex.what() << "\n";
}
}
Other Notes
Note that passing error_code errors can be done in two ways across idiomatic Asio interfaces, including Coroutines: How to set error_code to asio::yield_context
Full Sample
Simply adapting your sample to be selfcontained:
Live On Coliru
#include <iostream>
#include <boost/asio.hpp>
#include <boost/asio/spawn.hpp>
#include <boost/asio/high_resolution_timer.hpp>
using namespace std::chrono_literals;
int main() {
boost::asio::io_service loop;
spawn(loop, [&loop](boost::asio::yield_context yield)
{
try
{
spawn(yield, [&loop](boost::asio::yield_context yield)
{
boost::asio::high_resolution_timer timer{loop};
for(unsigned i = 0; i < 3; ++i)
{
std::cout << i * 2 + 1 << std::endl;
timer.expires_from_now(100ms);
timer.async_wait(yield);
}
throw std::system_error(ENOENT, std::system_category(), "Throw an error");
//throw boost::system::system_error(ENOENT, boost::system::system_category(), "Throw an error");
});
spawn(yield, [&loop](boost::asio::yield_context yield)
{
boost::asio::high_resolution_timer timer{loop};
for(unsigned i = 0; i < 3; ++i)
{
std::cout << (i + 1) * 2 << std::endl;
timer.expires_from_now(100ms);
timer.async_wait(yield);
}
});
} catch(const std::runtime_error& ex)
{
std::cerr << "L:" << __LINE__ << ": " << ex.what() << "\n";
}
});
while (true) {
try {
loop.run();
break;
} catch(std::runtime_error ex) {
std::cerr << "L:" << __LINE__ << ": " << ex.what() << "\n";
}
}
}
Prints
1
2
3
4
5
6
L:49: Throw an error: No such file or directory
BONUS:
Doing the extra approach with generalized competion token and async_result:
Live On Coliru
#include <iostream>
#include <boost/asio.hpp>
#include <boost/asio/spawn.hpp>
#include <boost/asio/high_resolution_timer.hpp>
using namespace std::chrono_literals;
boost::asio::io_service loop;
template <typename Token>
auto async_foo(bool success, Token&& token)
{
typename boost::asio::handler_type<Token, void(boost::system::error_code, int)>::type
handler (std::forward<Token> (token));
boost::asio::async_result<decltype (handler)> result (handler);
boost::asio::yield_context yield(token);
boost::asio::high_resolution_timer timer{loop};
for(unsigned i = 0; i < 3; ++i) {
std::cout << (i * 2 + (success?0:1)) << std::endl;
timer.expires_from_now(100ms);
timer.async_wait(yield);
}
if (success)
handler(42);
else
throw boost::system::system_error(ENOENT, boost::system::system_category(), "Throw an error");
return result.get();
}
int main() {
auto spawn_foo = [](bool success) {
spawn(loop, [=](boost::asio::yield_context yield) {
try
{
int answer = async_foo(success, yield);
std::cout << "async_foo returned " << answer << std::endl;
} catch(const std::runtime_error& ex)
{
std::cerr << "L:" << __LINE__ << ": " << ex.what() << std::endl;
}
});
};
spawn_foo(true);
spawn_foo(false);
loop.run();
}
Prints
0
1
2
3
4
5
async_foo returned 42
L:45: Throw an error: No such file or directory

boost::property_tree::ptree accessing array's first complex element

My JSON is this:
{
"apps":[
{
"id":"x",
"val":"y",
}
]
}
I can get id's value "x" by looping, and exiting when it.first is id:
for (const ptree::value_type &app : root.get_child("apps"))
{
for (const ptree::value_type &it : app.second) {
if (it.first == "id") {
std::cout << it.second.get_value<std::string>().c_str() << std::endl;
}
}
}
What I want, however, is to get the id's value by something like this:
std::cout << root.get<std::string>("apps[0].id").c_str() << std::endl;
Of course this displays nothing to me, for I am probably using wrong syntax accessing 1st element of the apps array. It might be that this has to be done in a different way all together.
I have found only this ugly and dangerous method:
std::cout << root.get_child("apps").begin()->second.begin()->second.get_value<std::string>().c_str() << std::endl;
I cannot really use it this way as it won't throw an exception when the array is empty, it will core dump!
Below is the whole program to make it easier for any one who wants to help:
#include <boost/property_tree/ptree.hpp>
#include <boost/property_tree/json_parser.hpp>
using boost::property_tree::ptree;
int main()
{
std::stringstream ss("{\"apps\":[{\"id\":\"x\",\"val\":\"y\"}]}");
ptree root;
read_json(ss, root);
for (const ptree::value_type &app : root.get_child("apps"))
{
for (const ptree::value_type &it : app.second) {
if (it.first == "id") {
std::cout << it.second.get_value<std::string>().c_str() << std::endl;
}
}
}
std::cout << root.get_child("apps").begin()->second.begin()->second.get_value<std::string>().c_str() << std::endl;
return 0;
}
As the docs say, array elements are nodes with "" keys.
If you're after the first element, you're in luck:
root.get("apps..id", "")
The .. in the path selects the first empty key
Live On Coliru
#include <boost/property_tree/json_parser.hpp>
#include <iostream>
using boost::property_tree::ptree;
int main() {
std::stringstream ss(R"({"apps":[{"id":"x","val":"y"}]})");
ptree root;
read_json(ss, root);
std::cout << root.get("apps..id", "") << "\n";
}
BONUS
If you need to address elements other than the first, write a helper function. This would be a good start:
#include <string>
#include <stdexcept> // std::out_of_range
template <typename Tree>
Tree query(Tree& pt, typename Tree::path_type path) {
if (path.empty())
return pt;
auto const head = path.reduce();
auto subscript = head.find('[');
auto name = head.substr(0, subscript);
auto index = std::string::npos != subscript && head.back() == ']'
? std::stoul(head.substr(subscript+1))
: 0u;
auto matches = pt.equal_range(name);
if (matches.first==matches.second)
throw std::out_of_range("name:" + name);
for (; matches.first != matches.second && index; --index)
++matches.first;
if (index || matches.first==matches.second)
throw std::out_of_range("index:" + head);
return query(matches.first->second, path);
}
Here's some live tests using it:
Live On Coliru
#include <boost/property_tree/json_parser.hpp>
#include <iostream>
using boost::property_tree::ptree;
int main() {
std::stringstream ss(R"({
"apps": [
{
"id": "x",
"val": "y",
"id": "hidden duplicate"
},
{
"id": "a",
"val": "b"
}
]
})");
ptree root;
read_json(ss, root);
for (auto path : {
"apps..id", "apps.[0].id", // (equivalent)
//
"apps.[0].id[]", // invalid
"apps.[0].id[0]", // x
"apps.[0].id[1]", // hidden duplicate
"apps.[1].id", // a
"apps.[1].id[0]", // a
"apps.[1].id[1]", // out of range
"apps.[2].id", // out of range
"drinks" // huh, no drinks at the foo bar
}) try {
std::cout << "Path '" << path << "' -> ";
std::cout << query(root, path).get_value<std::string>() << "\n";
} catch(std::exception const& e) {
std::cout << "Error: " << e.what() << "\n";
}
}
Prints:
Path 'apps..id' -> x
Path 'apps.[0].id' -> x
Path 'apps.[0].id[]' -> Error: stoul
Path 'apps.[0].id[0]' -> x
Path 'apps.[0].id[1]' -> hidden duplicate
Path 'apps.[1].id' -> a
Path 'apps.[1].id[0]' -> a
Path 'apps.[1].id[1]' -> Error: index:id[1]
Path 'apps.[2].id' -> Error: index:[2]
Path 'drinks' -> Error: name:drinks

boost::ptree find? or how to access deep arrays? C++

I have been trying for too much time now, to access a json_reader ptree from the boost library.
I have a json file that is capsulated really often: (pseudo-json:)
"Foo": {
"nameofFoo:"foofoo"
"Bar": [{
"BarFoo":
{ BarFooDeep: {
BarFooDeepDeep: {
"BarFooValue1": 123
"BarFooValue2" : 456
}
}
}
"FooBar": [ {
"FooBarDeep" :[ {
FooBarDeepDeep:[ {
FooBarValue1: "ineedthis"
FooBarValue2: "andthis"
} ]
FooBarDeepDeep1:[ {
FooBarValue1: "ineedthis"
FooBarValue2: "andthis"
} ]
"FooBarDeep" :[ {
FooBarDeepDeep2:[ {
FooBarValue1: "ineedthis"
FooBarValue2: "andthis"
} ]
FooBarDeepDeep3:[ {
FooBarValue1: "ineedthis"
FooBarValue2: "andthis"
} ]
and so on .... won t complete this now...
Now I only need to get FooBarValue1 and FooBarValue2 of all FooBar.
I know ptree puts arrays together with empty childs ("")
I can access all the members by itereration over all the childs recursively.
But is there not a better way to access special values?
how does ptree find works? i always get compiler errors ...
ptree jsonPT;
read_json( JSON_PATH, jsonPT);
ptree::const_iterator myIT = jsonPT.find("FooBarValue1");
double mlat = boost::lexical_cast<int>(myIT->second.data());
error: conversion from
‘boost::property_tree::basic_ptree,
std::basic_string >::assoc_iterator’ to non-scalar type
‘boost::property_tree::basic_ptree,
std::basic_string >::const_iterator’ requested
ptree::const_iterator myIT = jsonPT.find("FooBarValue1");
Can anyone give me a useful hint how to get access to this ptree?!?
As hinted in the linked answer I commented (Boost.PropertyTree subpath processing), you could write your own "selector" query, so you could write stuff like:
read_json("input.txt", pt);
std::ostream_iterator<std::string> out(std::cout, ", ");
std::cout << "\nSpecific children but in arrays: ";
enumerate_path(pt, "Foo.Bar..FooBar..FooBarDeep1..FooBarDeepDeep6..FooBarValue2", out);
std::cout << "\nSingle wildcard: ";
enumerate_path(pt, "Foo.Bar..FooBar..FooBarDeep1..*..FooBarValue2", out);
std::cout << "\nTwo wildcards: ";
enumerate_path(pt, "Foo.Bar..FooBar..*..*..FooBarValue2", out);
The enumerate_path function need not be too complicated and takes any output iterator (so you can back_inserter(some_vector) just as well):
template <typename Tree, typename Out, typename T = std::string>
Out enumerate_path(Tree const& pt, typename Tree::path_type path, Out out) {
if (path.empty())
return out;
if (path.single()) {
*out++ = pt.template get<T>(path);
} else {
auto head = path.reduce();
for (auto& child : pt) {
if (head == "*" || child.first == head) {
out = enumerate_path(child.second, path, out);
}
}
}
return out;
}
As simple working demo prints:
Specific children but in arrays: andthis6,
Single wildcard: andthis6, andthis7, andthis8, andthis9,
Two wildcards: andthis1, andthis2, andthis3, andthis4, andthis6, andthis7, andthis8, andthis9,
That is with the following input.txt:
{
"Foo": {
"nameofFoo": "foofoo",
"Bar": [{
"BarFoo": {
"BarFooDeep": {
"BarFooDeepDeep": {
"BarFooValue1": 123,
"BarFooValue2": 456
}
}
},
"FooBar": [{
"FooBarDeep0": [{
"FooBarDeepDeep1": [{
"FooBarValue1": "ineedthis1",
"FooBarValue2": "andthis1"
}],
"FooBarDeepDeep2": [{
"FooBarValue1": "ineedthis2",
"FooBarValue2": "andthis2"
}]
},
{
"FooBarDeepDeep3": [{
"FooBarValue1": "ineedthis3",
"FooBarValue2": "andthis3"
}],
"FooBarDeepDeep4": [{
"FooBarValue1": "ineedthis4",
"FooBarValue2": "andthis4"
}]
}],
"FooBarDeep1": [{
"FooBarDeepDeep6": [{
"FooBarValue1": "ineedthis6",
"FooBarValue2": "andthis6"
}],
"FooBarDeepDeep7": [{
"FooBarValue1": "ineedthis7",
"FooBarValue2": "andthis7"
}]
},
{
"FooBarDeepDeep8": [{
"FooBarValue1": "ineedthis8",
"FooBarValue2": "andthis8"
}],
"FooBarDeepDeep9": [{
"FooBarValue1": "ineedthis9",
"FooBarValue2": "andthis9"
}]
}]
}]
}]
}
}
Live On Coliru
Full Listing
Live On Coliru
#include <boost/property_tree/ptree.hpp>
#include <boost/property_tree/json_parser.hpp>
#include <iostream>
template <typename Tree, typename Out, typename T = std::string>
Out enumerate_path(Tree const& pt, typename Tree::path_type path, Out out) {
if (path.empty())
return out;
if (path.single()) {
*out++ = pt.template get<T>(path);
} else {
auto head = path.reduce();
for (auto& child : pt) {
if (head == "*" || child.first == head) {
out = enumerate_path(child.second, path, out);
}
}
}
return out;
}
int main() {
std::ostream_iterator<std::string> out(std::cout, ", ");
using namespace boost::property_tree;
ptree pt;
read_json("input.txt", pt);
std::cout << "\nSpecific children but in arrays: ";
enumerate_path(pt, "Foo.Bar..FooBar..FooBarDeep1..FooBarDeepDeep6..FooBarValue2", out);
std::cout << "\nSingle wildcard: ";
enumerate_path(pt, "Foo.Bar..FooBar..FooBarDeep1..*..FooBarValue2", out);
std::cout << "\nTwo wildcards: ";
enumerate_path(pt, "Foo.Bar..FooBar..*..*..FooBarValue2", out);
}
find() is for retrieving a child node by key; it doesn't search the whole ptree. It returns an assoc_iterator (or const_assoc_iterator), which you can convert to an iterator via the to_iterator() method on the parent:
ptree::const_assoc_iterator assoc = jsonPT.find("FooBarValue1");
ptree::const_iterator myIT = jsonPT.to_iterator(assoc);
To search the ptree, you'll need to iterate it recursively:
struct Searcher {
struct Path { std::string const& key; Path const* prev; };
void operator()(ptree const& node, Path const* path = nullptr) const {
auto it = node.find("FooBarValue1");
if (it == node.not_found()) {
for (auto const& child : node) { // depth-first search
Path next{child.first, path};
(*this)(child.second, &next);
}
} else { // found "FooBarValue1"
double mlat = boost::lexical_cast<int>(myIT->second.data());
// ...
std::cout << "Mlat: " << mlat << std::endl;
std::cout << "Path (reversed): ";
for (Path const* p = path; p != nullptr; p = p->prev)
std::cout << p << ".";
std::cout << std::endl;
}
}
};
Searcher{}(jsonPT);
Alternatives for writing the recursive traversal would be a C++14 generic lambda, or in C++11 a type-erased concrete lambda using std::function.