I'm using the Boost process header and I can't seem to get the boost::process::system to take in my .cpp file path due to a space in the directory.
auto path = bp::search_path("g++");
int result = bp::system(path, "\"C:\\Users\\Sachin Chopra\\Documents\\rchat\\console_process\\src\\main.cpp\"");
I get the following error when I execute my code:
g++.exe: error: "C:\Users\Sachin: Invalid argument
g++.exe: error: Chopra\Documents\rchat\console_process\src\main.cpp": No such file or directory
g++.exe: fatal error: no input files
The file path formatting works for me .exe file, and will launch from boost if I use it. e.g.
bp::system("\"C:\\Users\\Sachin Chopra\\Documents\\rchat\\build\\console_process\\consoleproc.exe\"");
But when I introduce the g++ path, it seems to mess up. Any help would be appreciated.
Cheers.
You're confusing shell script with the system interface.
You can either use old style, error-prone system:
bp::system(R"(bash -c "echo hello; echo world")");
Or you can pass raw arguments instead of relyng on shell escaping
bp::system(bp::search_path("bash"),
std::vector<std::string>{
"-c",
"echo foo; echo bar",
});
Which could also be written more like
bp::system(bp::search_path("bash"), "-c", "echo 42; echo answer");
In fact, you should probably use the bp::child interace instead of the
system compatible one:
bp::child compiler_job(
bp::search_path("g++"),
R"(C:\Users\Sachin Chopra\Documents\rchat\console_process\src\main.cpp)");
compiler_job.wait_for(5s);
if (compiler_job.running()) {
compiler_job.terminate();
}
int result = compiler_job.exit_code();
std::cout << "compiler_job exit_code: " << result << "\n";
Live Demo
Live On Coliru
#include <boost/process.hpp>
#include <iostream>
namespace bp = boost::process;
using namespace std::chrono_literals;
int main() {
// either use old style, error-prone system
bp::system(R"(bash -c "echo hello; echo world")");
// or pass raw arguments instead of relyng on shell escaping
bp::system(bp::search_path("bash"),
std::vector<std::string>{
"-c",
"echo foo; echo bar",
});
// Which can aslo be written as
bp::system(bp::search_path("bash"), "-c", "echo 42; echo answer");
// in fact, you should probably use the `bp::child` interace instead of the
// `system` compatible one:
bp::child compiler_job(
bp::search_path("g++"),
R"(C:\Users\Sachin Chopra\Documents\rchat\console_process\src\main.cpp)");
compiler_job.wait_for(5s);
if (compiler_job.running()) {
compiler_job.terminate();
}
int result = compiler_job.exit_code();
std::cout << "compiler_job exit_code: " << result << "\n";
}
Prints e.g.
hello
world
foo
bar
42
answer
g++: error: C:\Users\Sachin Chopra\Documents\rchat\console_process\src\main.cpp: No such file or directory
g++: fatal error: no input files
compilation terminated.
compiler_job exit_code: 1
Related
Using this very nice Commandline parser Argo (Header only C++ library) I've encountered a small issue.
See : https://github.com/phforest/Argo
Argo returns : 'Error: Unknown option' when a option in not found, but not when the argument is behind a know argument.
Compiling the code below: (inc is location of the argo header)
c++ test.cpp -I inc --std=c++11
#include <iostream>
int main(int argc, char **argv)
{
argo::Configuration pcnfg;
std::vector<std::string> input_texts;
pcnfg.program.name = { "wow", "EyeOnText WoWoolConsole" };
pcnfg.program.version = { 1, 1, 1 };
argo::Arguments args(pcnfg);
args.add(argo::handler::Option("input-text", "i", input_texts).help("Input text to process."));
const auto result = args.parse(argc, argv);
switch (result.status)
{
case argo::ReturnCode::Error: std::cerr << "Error: " << result.message << std::endl; return 1;
case argo::ReturnCode::SuccessAndAbort: return 0;
default: break;
}
for ( auto const & input : input_texts )
{
std::cout << "- " << input << std::endl;
}
return 0;
}
run:
./a.out --other -i "test"
Error: Unknown option '--other'
Which is ok
run:
./a.out -i "test" --other
- test
- --other
--other should not be in the input list.
(Disclaimer: I'm the developer of the library)
I think this is solved in more recent versions. At least, using the provided code, I get the expected output (twice an 'Unknown option' error). If it's not solved, we can take it up using the bug tracker at https://gitlab.com/dgrine/Argo/issues
I have found this code as a bash autocomplete. But, it looks strange to me. What if I do not like to run the code at all. If I would like to type ./a.out then space (without entering) and then by pressing tab, I would like to see only two options apple and cherry and if I type a and press tab, then it autocomplete the option apple and similarly for c. Let's say only one of the two options are acceptable:
./a.out apple
./a.out cherry
where apple and cherry are options and not the name of the files in the directory. In the first case, I would like the program types that your option is apple and in the second case your option is cherry. In any other case, the program should print an error that the option is not valid.
All examples that I find on the internet such as what follows look like that you should run the program first, then it reacts. The while loop inside the main function collides with the normal functionality of the program. Have I misunderstood the readline library? Is the above-described application possible to implement by editing the following code?
// sudo apt-get install libreadline-dev
// g++ -std=c++11 main.cpp -lreadline
#include <iostream>
#include "readline/readline.h"
#include "readline/history.h"
using namespace std;
int main(int argc, char** argv)
{
const char *line;
while ((line = readline("? ")) != nullptr) {
cout << "[" << line << "]" << endl;
if (*line) add_history(line);
free(line);
}
// if(argc!=2)
// {
// cout<<"<exe> one_parameter"<<endl;
// return 1;
// }
// string option=argv[1];
// if(option=="apple" || option=="cherry")
// cout<<"Your option is "<<option<<endl;
// else
// {
// cout<<"Error: invalid option "<<option<<endl;
// return 1;
// }
return 0;
}
// partial answer - why you may want to invoke the app while doing the autocompletion
One way of implementing the autocomplete for an application is to have the application binary configure it (by having a flag that prints the instructions for autocomplete configuration or by just parsing the --help output of the application).
Schemataically:
complete -F $(./a.out --generate-autocomplete-config) ./a.out
This is why you might see the binary actually invoked as a part of autocomplete implementation.
This has nothing to do with your executable. You need to put this in a file and source (source autocomplete_file or . autocomplete_file) it in the bash.
_a_complete_()
{
local word=${COMP_WORDS[COMP_CWORD]}
local files='apple cherry'
COMPREPLY=( $( compgen -W "${files}" -- ${word} ) )
}
complete -F _a_complete_ ./a.out
Here a nice documentation can be found.
I am trying to run rsync through execvp with StrictHostKeyChecking option.
This is my code:
#include <unistd.h>
int main() {
char *argv[] = {"rsync",
"remote_user#1.2.4.5:/tmp",
"/home/tmp/",
"-e 'ssh -o StrictHostKeyChecking=no'",
0};
execvp("rsync", argv);
}
I am getting this error:
rsync: -e '-o StrictHostKeyChecking=no': unknown option
rsync error: syntax or usage error (code 1) at main.c(1422) [client=3.0.6]
I have tried another way:
#include <unistd.h>
int main() {
char *argv[] = {"rsync",
"remote_user#1.2.4.5:/tmp",
"/home/tmp/",
"-e",
"'ssh -o StrictHostKeyChecking=no'",
0};
execvp("rsync", argv);
}
But now it is failing with error:
rsync: Failed to exec ssh -o StrictHostKeyChecking=no: No such file or directory (2) rsync error: error in IPC code (code 14) at pipe.c(84) [sender=3.0.6]
Why it don't understand StrictHostKeyChecking option?
rsync expects to receive the options first, followed by the hosts. You're doing it backwards: first you need to specify -e and -o.
You also shouldn't be single-quoting the -o option: that is needed when invoking it from bash, to prevent bash from interpreting the arguments and splitting them into separate argv[] entries. Think about it: when bash sees '-o StrictHostKeyChecking=no', it passes -o StrictHostKeyChecking=no as a single argv[] entry, without the single quotes (because the single quotes is your way to tell the shell that you don't want argument splitting).
Last, but not least, you should check that execvp(3) didn't fail.
So, this is what you need:
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
int main() {
char *argv[] = {
"rsync",
"-e",
"ssh -o StrictHostKeyChecking=no",
"remote_user#1.2.4.5:/tmp",
"/home/tmp/",
NULL
};
if (execvp("rsync", argv) < 0) {
perror("rsync(1) error");
exit(EXIT_FAILURE);
}
return 0;
}
I'm using boost::program_options to get parameters from a config file.
i understand that i can create a file by hand and program options will parse it. but i'm looking for a way for the program to generate the file automatically. meaning printing out the name of the option and it's value. for example:
>./main
without option would generate init.cfg that looks like this
[wave packet]
width = 1
position = 2.0
[calculation parameters]
levels = 15
then i would go into that file change the values using text editor and use this file:
>./main init.cfg
a nice way to approach this would be to have variables_map to have operator<<. this way i can just write it to file. change the values. read the file. all in the same format and no need to write each line.
i couldn't find anything like that in documentation or examples. please let me know if this is possible
EDIT: Sam Miller showed how to parse the ini file in sections. However, I still have a problem getting the values from boost::program_options::variables_map vm.
i tried the following
for(po::variables_map::iterator it = vm.begin(); it != vm.end(); ++it)
{
if(it->first!="help"&&it->first!="config")
cout << "first - " << it->first << ", second - " << it->second.value() << "\n";
}
instead of it->second.value(), got an error. i also tried it->second. i also got an error:
icpc -lboost_serialization -lboost_program_options -c programOptions.cc
programOptions.cc(60): error: no operator "<<" matches these operands
operand types are: std::basic_ostream<char, std::char_traits<char>> << boost::any
cout << "first - " << it->first << ", second - " << it->second.value() << "\n";
^
compilation aborted for programOptions.cc (code 2)
make: *** [programOptions.o] Error 2
i get the value correctly if i use it->second.as<int>() but not all of my values are ints and once i reach double, the program crashes with this:
terminate called after throwing an instance of 'boost::exception_detail::clone_impl<boost::exception_detail::error_info_injector<boost::bad_any_cast> >'
what(): boost::bad_any_cast: failed conversion using boost::any_cast
There's not a way using program_options that I'm aware of. You could use the property tree library to write the ini file.
Here is a short example:
macmini:stackoverflow samm$ cat property.cc
#include <boost/property_tree/ptree.hpp>
#include <boost/property_tree/ini_parser.hpp>
#include <iostream>
int
main()
{
using boost::property_tree::ptree;
ptree root;
ptree wave_packet;
wave_packet.put( "width", "1" );
wave_packet.put( "position", "2.0" );
ptree calculation_parameters;
calculation_parameters.put( "levels", "15" );
root.push_front(
ptree::value_type( "calculation parameters", calculation_parameters )
);
root.push_front(
ptree::value_type( "wave packet", wave_packet )
);
write_ini( std::cout, root );
return 0;
}
macmini:stackoverflow samm$ g++ property.cc
macmini:stackoverflow samm$ ./a.out
[wave packet]
width=1
position=2.0
[calculation parameters]
levels=15
macmini:stackoverflow samm$
As far as I understand the question, it is about how to write config file based on given option_description.
Here is the possible, solution, how to write one options_description to config file. It relates on the fact that every parameter has some default value.
void SaveDefaultConfig()
{
boost::filesystem::ofstream configFile(configFilePath_);
auto descOptions = algorithmsDesc_.options();
boost::property_tree::ptree tree;
for (auto& option : descOptions)
{
std::string name = option->long_name();
boost::any defaultValue;
option->semantic()->apply_default(defaultValue);
if (defaultValue.type() == typeid(std::string))
{
std::string val = boost::any_cast<std::string>(defaultValue);
tree.put(name, val);
}
///Add here additional else.. type() == typeid() if neccesary
}
//or write_ini
boost::property_tree::write_json(configFile, tree);
}
Here algorithmsDesc is boost::program_options::options_description, that is where you describe options like:
algorithmsDesc_.add_options()
("general.blur_Width", po::value<int>(&varWhereToStoreValue)->default_value(3), "Gaussian blur aperture width")
The problem is if you need sections in config file. options_description doesn't have method to get caption passed through it's constructor. The dirty way to get it is to cut it from output stream made by print():
std::string getSectionName()
{
std::stringstream ss;
algorithmDesc_.print(ss)
std::string caption;
std::getline(ss,caption)
//cut last ':'
return caption.substr(0, caption.size() - 1)
}
Combining them together is straightforward.
I am looking for a complete i18n gettext() hello world example. I have started a script based upon A tutorial on Native Language Support using GNU gettext by G. Mohanty. I am using Linux and G++.
Code:
cat >hellogt.cxx <<EOF
// hellogt.cxx
#include <libintl.h>
#include <locale.h>
#include <iostream>
#include <cstdlib>
int main (){
char* cwd = getenv("PWD");
std::cout << "getenv(PWD): " << (cwd?cwd:"NULL") << std::endl;
char* l = getenv("LANG");
std::cout << "getenv(LANG): " << (l?l:"NULL") << std::endl;
char* s = setlocale(LC_ALL, "");
std::cout << "setlocale(): " << (s?s:"NULL") << std::endl;
std::cout << "bindtextdomain(): " << bindtextdomain("hellogt", cwd) << std::endl;
std::cout << "textdomain(): " << textdomain( "hellogt") << std::endl;
std::cout << gettext("hello, world!") << std::endl;
}
EOF
g++ -ohellogt hellogt.cxx
xgettext -d hellogt -o hellogt.pot hellogt.cxx
msginit --no-translator -l es_MX -o hellogt_spanish.po -i hellogt.pot
sed --in-place hellogt_spanish.po --expression='/#: /,$ s/""/"hola mundo"/'
sed --in-place hellogt_spanish.po --expression='s/PACKAGE VERSION/hellogt 1.0/'
mkdir -p ./es_MX/LC_MESSAGES
msgfmt -c -v -o ./es_MX/LC_MESSAGES/hellogt.mo hellogt_spanish.po
export LANG=es_MX
ls -l $PWD/es_MX/LC_MESSAGES/hellogt.mo
./hellogt
strace -e trace=open ./hellogt
The program compiles, the text is extracted, Spanish file is created, modified and binary created but hellogt still displays English. The trace shows no evidence of looking in the current working directory for es_MX nor any references to LC_MESSAGES directory.
Your problem is that hellogt.mo is in the wrong location - your program isn't actually opening it. You can tell this by using strace to trace open syscalls:
strace -e trace=open ./hellogt
...
open("/tmp/.//es_MX/LC_MESSAGES/hellogt.mo", O_RDONLY) = -1 ENOENT (No such file or directory)
open("/tmp/.//es/LC_MESSAGES/hellogt.mo", O_RDONLY) = -1 ENOENT (No such file or directory)
You can affect where gettext looks for message catalogs with the LOCPATH environment variable, but if you move it to where gettext is attempting to load it from your example works:
mkdir -p es/LC_MESSAGES
cp hellogt.mo es/LC_MESSAGES
./hellogt
hola mundo
cat >hellogt.cxx <<EOF
// hellogt.cxx
#include <libintl.h>
#include <locale.h>
#include <iostream>
int main (){
setlocale(LC_ALL, "");
bindtextdomain("hellogt", ".");
textdomain( "hellogt");
std::cout << gettext("hello, world!") << std::endl;
}
EOF
g++ -o hellogt hellogt.cxx
xgettext --package-name hellogt --package-version 1.2 --default-domain hellogt --output hellogt.pot hellogt.cxx
msginit --no-translator --locale es_MX --output-file hellogt_spanish.po --input hellogt.pot
sed --in-place hellogt_spanish.po --expression='/"hello, world!"/,/#: / s/""/"hola mundo"/'
mkdir --parents ./es_MX.utf8/LC_MESSAGES
msgfmt --check --verbose --output-file ./es_MX.utf8/LC_MESSAGES/hellogt.mo hellogt_spanish.po
LANGUAGE=es_MX.utf8 ./hellogt
Here is a description of the files created by the above:
hellogt.cxx C++ source file
hellogt Executable image
hellogt.pot Extracted text from C++ source file (portable object template)
hellogt_spanish.po Modified text for Spanish with translations added (using sed)
es_MX.utf8/
LC_MESSAGES/
hellogt.mo Binary translated text for Spanish used at run-time
Here is a description of gettext from Fedora Project. It is simple to follow. But it is in C.
http://fedoraproject.org/wiki/How_to_do_I18N_through_gettext