Related
Problem
I am trying to convert a string to a C string. In doing so, I need to replace " with \". I use the following code to do this:
QString Converter::plain2C(const QString &in) {
QString out;
// Split input in each line
QStringList list = in.split(QChar('\n'));
for (int i = 0; list.length() > i; i++) { // Go throught each line of the input
QString line = list[i]; // Thats the line
line.replace(QChar('\\'), QLatin1String("\\\\")); // replace \ with \\
line.replace(QChar('"'), QLatin1String("\\\"")); // replace " with \"
// For printf()
if(escapePercent)
line.replace(QChar('%'), QLatin1String("%%"));
// If option "Split output into multiple lines" is active add a " to the output
if (multiLine)
out.append(QChar('"'));
// append the line to the output
out.append(line);
// append a "\n" to the output because we are at the end of a line
if (list.length() -1 > i)
out.append(QLatin1String("\\n"));
// If option "Split output into multiple lines" is active add a " and \n to the output
if (multiLine) {
out.append(QChar('"'));
out.append(QChar('\n'));
}
}
if (!multiLine) {
out.prepend(QChar('"'));
out.append(QChar('"'));
}
return out;
}
However, " is still there without a \ before.
Information
Qt Version 5.15.3
C++17
Edit
The application is used to enter a normal string copied from the Internet and get as a result a string that can be copied into a C/C++ program.
More code
void Converter::run()
{
if (_from != From::NotSupportet &&
_to != To::toInvalid) {
QString out;
// More code obove and below
else if (_from == From::Plain) {
switch (_to) {
case To::toCString:
out = plain2C(_in);
break;
// Emits the ready string which is applied direct to a QPlainTextEdit
emit htmlReady(out);
}
}
Edit 2
Added more comments to the code
It works now. The problem was the line above:
line.replace(QChar('\'), QLatin1String("\\\\")); // replace \ with \\
The problem was that the comment ended with 2 \. That somehow disabled the next line or something like that.
Anyway, this is the working code:
QString Converter::plain2C(const QString &in) {
QString out;
// Split input in each line
QStringList list = in.split(QChar('\n'));
for (int i = 0; list.length() > i; i++) { // Go throught each line of the input
QString line = list[i]; // Thats the line
line.replace(QChar('\\'), QLatin1String("\\\\")); // replace "\" with "\\"
line.replace(QChar('"'), QLatin1String("\\\"")); // replace " with \"
// For printf()
if(escapePercent)
line.replace(QChar('%'), QLatin1String("%%"));
// If option "Split output into multiple lines" is active add a " to the output
if (multiLine)
out.append(QChar('"'));
// append the line to the output
out.append(line);
// append a "\n" to the output because we are at the end of a line
if (list.length() -1 > i)
out.append(QLatin1String("\\n"));
// If option "Split output into multiple lines" is active add a " and \n to the output
if (multiLine) {
out.append(QChar('"'));
out.append(QChar('\n'));
}
}
if (!multiLine) {
out.prepend(QChar('"'));
out.append(QChar('"'));
}
return out;
}
Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 11 months ago.
Improve this question
How to make the c ++ application work with the browser. I mean a program that retrieves data from a given page (let's assume that the page displays a string) and then performs some reaction on the page. For example, the page displays a random string, and the program enters the length of the string into the form.
I am a novice programmer, so I care about information and advice on where to start. Thanks in advance for any help.
As I already promised to OP in comments, posting Partial answer, which doesn't answer all questions, but only provides handy tool to wrap (call) any Python code inside C++ program.
In my code snippet I don't even do anything with browsers, but instead show only example of computing Greatest Common Divisor using Python's standard function math.gcd().
I decided to introduce this Python-in-C++ bridge only because there exist many beautiful Python modules that work with browsers or with parsing/composing HTML, hence it is much easier to write such tools in Python instead of C++.
But expert without knowledge of default Python C API, it is not that easy to implement even simple use case - compile text of Python code, pass to it any arguments from C++, receive response arguments, return arguments back to C++. Only these simple actions need usage of a dozen of different Python C API functions. That's why I decided to show how to do it, as I know.
I implemented from scratch (specifically for OP's question) handy class PyRunner which does all the magic, usage of this class is simple:
PyRunner pyrun;
std::string code = R"(
def gcd(a, b):
import math
return math.gcd(a, b)
res = gcd(*arg)
print('GCD of', arg[0], 'and', arg[1], 'is', res, flush = True)
)";
std::cout << pyrun.Run(code, "(2 * 3 * 5, 2 * 3 * 7)") << std::endl;
std::cout << pyrun.Run(code, "(5 * 7 * 11, 5 * 7 * 13)") << std::endl;
Basically you just pass any Python code snippet to PyRunner::Run() method and also any argument (represented as Python object converted to string). Result of this call is also a returned Python object converted to string. You can also use JSON to pass any large argument as string and parse returned argument, as any JSON string is also a valid stringized Python object.
Of course you need a knowledge of Python to be able to write complex code snippets inside C++.
One drawback of my PyRunner class is that for some reason (that I didn't yet understand), you can't import Python module inside global scope, as you can see I did import math within function scope. But this is not a big deal, I think, and maybe some experts will clarify the reason.
To compile and run code you need to have pre-installed Python, and pass Python's include folder and library file as compiler arguments. For example in Windows CLang you do following:
clang.exe -std=c++20 -O3 -Id:/bin/Python39/include/ d:/bin/Python39/libs/python39.lib prog.cpp
and in Linux:
clang -std=c++20 -O3 -I/usr/include/ -lpython3.9 prog.cpp
To run the program either you should provide environment variables PYTHONHOME or PYTHONPATH or run program from Python folder (like d:/bin/Python39/) or do sys.path.append("d:/bin/Python39/") on first lines of Python code snippet embedded in C++. Without these paths Python can't find location of its standard library.
PyRunner class is thread-safe, but only single-threaded always. It means that two calls to .Run() inside two threads will be exclusively blocked by mutex. I use std::mutex instead of Python's GIL to protect from multi-threading, because it is quite alright (and faster), if you don't use Python C API in any other threads simultaneously. Also it is not allowed right now to have two instances of PyRunner objects as it does Py_Initialize() and Py_FinalizeEx() in constructor and destructor, which should be done globally only once. Hence PyRunner should be a singleton.
Below is full C++ code with implementation of PyRunner class and its usage (usage is inside main()). See console output after code below. Click Try it online! link to see compile/run of this code on free GodBolt online Linux servers.
Try it online!
#include <iostream>
#include <functional>
#include <string>
#include <string_view>
#include <stdexcept>
#include <memory>
#include <mutex>
#include <Python.h>
#define ASSERT_MSG(cond, msg) { if (!(cond)) throw std::runtime_error("Assertion (" #cond ") failed at line " + std::to_string(__LINE__) + "! Msg: '" + std::string(msg) + "'."); }
#define ASSERT(cond) ASSERT_MSG(cond, "")
#define PY_ASSERT_MSG(cond, msg) { if (!(cond) || PyErr_Occurred()) { PyErr_Print(); ASSERT_MSG(false && #cond, msg); } }
#define PY_ASSERT(cond) PY_ASSERT_MSG(cond, "")
#define LN { std::cout << "LN " << __LINE__ << std::endl << std::flush; }
class PyRunner {
private:
class PyObj {
public:
PyObj(PyObject * pobj, bool inc_ref = false) : p_(pobj) {
if (inc_ref)
Py_XINCREF(p_);
PY_ASSERT_MSG(p_, "NULL PyObject* passed!");
}
PyObject * Get() { return p_; }
~PyObj() {
Py_XDECREF(p_);
p_ = nullptr;
}
private:
PyObject * p_ = nullptr;
};
public:
PyRunner() {
Py_SetProgramName(L"prog.py");
Py_Initialize();
}
~PyRunner() {
codes_.clear();
Py_FinalizeEx();
}
std::string Run(std::string code, std::string const & arg = "None") {
std::unique_lock<std::mutex> lock(mutex_);
code = StrUnIndent(code);
if (!codes_.count(code))
codes_.insert(std::pair{code, std::make_shared<PyObj>(Py_CompileString(code.c_str(), "script.py", Py_file_input))});
PyObj & compiled = *codes_.at(code);
PyObj globals_arg_mod = PyModule_New("arg"), globals_arg = PyModule_GetDict(globals_arg_mod.Get()), locals_arg = PyDict_New(),
globals_mod = PyModule_New("__main__"), globals = PyModule_GetDict(globals_mod.Get()), locals = PyDict_New();
// py_arg = PyUnicode_FromString(arg.c_str()),
PyObj py_arg = PyRun_String(arg.c_str(), Py_eval_input, globals_arg.Get(), locals_arg.Get());
PY_ASSERT(PyDict_SetItemString(locals.Get(), "arg", py_arg.Get()) == 0);
#if 0
PyObj result = PyEval_EvalCode(compiled.Get(), globals.Get(), locals.Get());
#else
PyObj builtins(PyEval_GetBuiltins(), true), exec(PyDict_GetItemString(builtins.Get(), "exec"), true);
PyObj exec_args = PyTuple_Pack(3, compiled.Get(), globals.Get(), locals.Get());
PyObj result = PyObject_CallObject(exec.Get(), exec_args.Get());
#endif
PyObj res(PyDict_GetItemString(locals.Get(), "res"), true), res_str = PyObject_Str(res.Get());
char const * cres = nullptr;
PY_ASSERT(cres = PyUnicode_AsUTF8(res_str.Get()));
return cres;
}
private:
static std::string StrUnIndent(std::string_view const & s) {
auto lines = StrSplit(s, "\n");
size_t min_off = size_t(-1);
for (auto const & line: lines) {
if (StrTrim(line).empty())
continue;
min_off = std::min<size_t>(min_off, line.find_first_not_of("\t\n\v\f\r "));
}
ASSERT(min_off < 10000ULL);
std::string res;
for (auto const & line: lines)
res += line.substr(std::min<size_t>(min_off, line.size())) + "\n";
return res;
}
static std::string StrTrim(std::string s) {
s.erase(0, s.find_first_not_of("\t\n\v\f\r ")); // left trim
s.erase(s.find_last_not_of("\t\n\v\f\r ") + 1); // right trim
return s;
}
static std::vector<std::string> StrSplit(std::string_view const & s, std::string_view const & delim) {
std::vector<std::string> res;
size_t start = 0;
while (true) {
size_t pos = s.find(delim, start);
if (pos == std::string::npos)
pos = s.size();
res.emplace_back(s.substr(start, pos - start));
if (pos >= s.size())
break;
start = pos + delim.size();
}
return res;
}
private:
std::unordered_map<std::string, std::shared_ptr<PyObj>> codes_;
std::mutex mutex_;
};
int main() {
try {
PyRunner pyrun;
std::string code = R"(
def gcd(a, b):
import math
return math.gcd(a, b)
res = gcd(*arg)
print('GCD of', arg[0], 'and', arg[1], 'is', res, flush = True)
)";
std::cout << pyrun.Run(code, "(2 * 3 * 5, 2 * 3 * 7)") << std::endl;
std::cout << pyrun.Run(code, "(5 * 7 * 11, 5 * 7 * 13)") << std::endl;
return 0;
} catch (std::exception const & ex) {
std::cout << "Exception: " << ex.what() << std::endl;
return -1;
}
}
Console output:
GCD of 30 and 42 is 6
6
GCD of 385 and 455 is 35
35
I'm relatively new to c++, mostly worked with python.
I have a scenario where a user(me) uses a GUI to send commands to a microcontroller via serial, and then the microcontroller processes them.
Right now i have 10 commands, but as the project develops (some form of modular robot) I can envision having 50-100 possible commands.
Is there a better way for my c++ handleCommands function to select which one of the possible 100 functions to run without doing massive case switches or if else statements?
Extract of the code:
char cmd = 1; // example place holder
int value = 10; //example place holder
switch (cmd){
case '1':
toggleBlink(value);
break;
case '2':
getID(value); // in this case value gets ignored by the function as its not required
break;
This works fine for 3-4 functions but doesn't seem to me like the best way to do it for more functions.
I've heard of lookup tables but as each function is different and may require arguments or not I'm consumed on how to implement them.
Some background on the set-up:
The commands are mainly diagnostic ,< ID > ect and a couple of functional ones that require parameters like, <blink,10> <runto,90> <set-mode,locked>
The validation is done in python against a csv file and the actual serial message sent to the microcontroller is sent as <(index of comand in csvfile),parameter> with < > and , being delimiters.
So the user would type blink,10 and the python app will send <1,10> over serial as blink is found at index 1 of the csv file.
The microcontroller reads these in and i am left over with 2 char arrays, the command array containing a number, and the value array containing the value sent.(also a number)
As I'm running this on a microcontroller i don't really want to have to store a long file of possible commands in flash, hence the validation done on the python gui side.
Note that in the case of a possible multi argument function, say <move,90,30> i.e move 90 degrees in 30 seconds eat, the actual function would only receive one argument "30,90" and then split that up as needed.
If you have the commands comming over the serial line in the format
<command-mapped-to-a-number,...comma-separated-parameters...>
we can simulate that like so:
#include <iostream>
#include <sstream> // needed for simple parsing
#include <string>
#include <unordered_map> // needed for mapping of commands to functors
int main() {
std::cout << std::boolalpha;
// example commands lines read from serial:
for (auto& cmdline : {"<1,10>", "<2,10,90>", "<3,locked>", "<4>"}) {
std::cout << exec(cmdline) << '\n';
}
}
exec above is the interpreter that will return true if the command line was parsed and executed ok. In the examples above, command 1 takes one parameter, 2 takes two, 3 takes one (string) and 4 doesn't have a parameter.
The mapping from command-mapped-to-a-number could be an enum:
// uint8_t has room for 256 commands, make it uint16_t to get room for 65536 commands
enum class command_t : uint8_t {
blink = 1,
take_two = 2,
set_mode = 3,
no_param = 4,
};
and exec would make the most basic validation of the command line (checking < and >) and put it in a std::istringstream for easy extraction of the information on this command line:
bool exec(const std::string& cmdline) {
if(cmdline.size() < 2 || cmdline.front() != '<' || cmdline.back() != '>' )
return false;
// put all but `<` and `>` in an istringstream:
std::istringstream is(cmdline.substr(1,cmdline.size()-2));
// extract the command number
if (int cmd; is >> cmd) {
// look-up the command number in an `unordered_map` that is mapped to a functor
// that takes a reference to an `istringstream` as an argument:
if (auto cit = commands.find(command_t(cmd)); cit != commands.end()) {
// call the correct functor with the rest of the command line
// so that it can extract, validate and use the arguments:
return cit->second(is);
}
return false; // command look-up failed
}
return false; // command number extraction failed
}
The only tricky part left is the unordered_map of commands and functors.
Here's a start:
// a helper to eat commas from the command line
struct comma_eater {} comma;
std::istream& operator>>(std::istream& is, const comma_eater&) {
// next character must be a comma or else the istream's failbit is set
if(is.peek() == ',') is.ignore();
else is.setstate(std::ios::failbit);
return is;
}
std::unordered_map<command_t, bool (*)(std::istringstream&)> commands{
{command_t::blink,
[](std::istringstream& is) {
if (int i; is >> comma >> i && is.eof()) {
std::cout << "<blink," << i << "> ";
return true;
}
return false;
}},
{command_t::take_two,
[](std::istringstream& is) {
if (int a, b; is >> comma >> a >> comma >> b && is.eof()) {
std::cout << "<take-two," << a << ',' << b << "> ";
return true;
}
return false;
}},
{command_t::set_mode,
[](std::istringstream& is) {
if (std::string mode; is >> comma && std::getline(is, mode,',') && is.eof()) {
std::cout << "<set-mode," << mode << "> ";
return true;
}
return false;
}},
{command_t::no_param,
[](std::istringstream& is) {
if (is.eof()) {
std::cout << "<no-param> ";
return true;
}
return false;
}},
};
If you put that together you'll get the below output from the successful parsing (and execution) of all command lines received:
<blink,10> true
<take-two,10,90> true
<set-mode,locked> true
<no-param> true
Here's a live demo.
Given an integer index for each "command" a simple function pointer look-up table can be used. For example:
#include <cstdio>
namespace
{
// Command functions (dummy examples)
int examleCmdFunctionNoArgs() ;
int examleCmdFunction1Arg( int arg1 ) ;
int examleCmdFunction2Args( int arg1, int arg2 ) ;
int examleCmdFunction3Args( int arg1, int arg2, arg3 ) ;
int examleCmdFunction4Args( int arg1, int arg2, int arg3, int arg4 ) ;
const int MAX_ARGS = 4 ;
const int MAX_CMD_LEN = 32 ;
typedef int (*tCmdFn)( int, int, int, int ) ;
// Symbol table
#define CMD( f ) reinterpret_cast<tCmdFn>(f)
static const tCmdFn cmd_lookup[] =
{
0, // Invalid command
CMD( examleCmdFunctionNoArgs ),
CMD( examleCmdFunction1Arg ),
CMD( examleCmdFunction2Args ),
CMD( examleCmdFunction3Args ),
CMD( examleCmdFunction4Args )
} ;
}
namespace cmd
{
// For commands of the form: "<cmd_index[,arg1[,arg2[,arg3[,arg4]]]]>"
// i.e an angle bracketed comma-delimited sequence commprising a command
// index followed by zero or morearguments.
// e.g.: "<1,123,456,0>"
int execute( const char* command )
{
int ret = 0 ;
int argv[MAX_ARGS] = {0} ;
int cmd_index = 0 ;
int tokens = std::sscanf( "<%d,%d,%d,%d,%d>", command, &cmd_index, &argv[0], &argv[1], &argv[2], &argv[3] ) ;
if( tokens > 0 && cmd_index < sizeof(cmd_lookup) / sizeof(*cmd_lookup) )
{
if( cmd_index > 0 )
{
ret = cmd_lookup[cmd_index]( argv[0], argv[1], argv[2], argv[3] ) ;
}
}
return ret ;
}
}
The command execution passes four arguments (you can expand that as necessary) but for command functions taking fewer arguments they will simply be "dummy" arguments that will be ignored.
Your proposed translation to an index is somewhat error prone and maintenance heavy since it requires you to maintain both the PC application symbol table and the embedded look up table in sync. It may not be prohibitive to have the symbol table on the embedded target; for example:
#include <cstdio>
#include <cstring>
namespace
{
// Command functions (dummy examples)
int examleCmdFunctionNoArgs() ;
int examleCmdFunction1Arg( int arg1 ) ;
int examleCmdFunction2Args( int arg1, int arg2 ) ;
int examleCmdFunction3Args( int arg1, int arg2, arg3 ) ;
int examleCmdFunction4Args( int arg1, int arg2, int arg3, int arg4 ) ;
const int MAX_ARGS = 4 ;
const int MAX_CMD_LEN = 32 ;
typedef int (*tCmdFn)( int, int, int, int ) ;
// Symbol table
#define SYM( c, f ) {#c, reinterpret_cast<tCmdFn>(f)}
static const struct
{
const char* symbol ;
const tCmdFn command ;
} symbol_table[] =
{
SYM( cmd0, examleCmdFunctionNoArgs ),
SYM( cmd1, examleCmdFunction1Arg ),
SYM( cmd2, examleCmdFunction2Args ),
SYM( cmd3, examleCmdFunction3Args ),
SYM( cmd4, examleCmdFunction4Args )
} ;
}
namespace cmd
{
// For commands of the form: "cmd[ arg1[, arg2[, arg3[, arg4]]]]"
// i.e a command string followed by zero or more comma-delimited arguments
// e.g.: "cmd3 123, 456, 0"
int execute( const char* command_line )
{
int ret = 0 ;
int argv[MAX_ARGS] = {0} ;
char cmd[MAX_CMD_LEN + 1] ;
int tokens = std::sscanf( "%s %d,%d,%d,%d", command_line, cmd, &argv[0], &argv[1], &argv[2], &argv[3] ) ;
if( tokens > 0 )
{
bool cmd_found = false ;
for( int i = 0;
!cmd_found && i < sizeof(symbol_table) / sizeof(*symbol_table);
i++ )
{
cmd_found = std::strcmp( cmd, symbol_table[i].symbol ) == 0 ;
if( cmd_found )
{
ret = symbol_table[i].command( argv[0], argv[1], argv[2], argv[3] ) ;
}
}
}
return ret ;
}
}
For very large symbol tables you might want a more sophisticated look-up, but depending on the required performance and determinism, the simple exhaustive search will be sufficient - far faster than the time taken to send the serial data.
Whilst the resource requirement for the symbol table is somewhat higher that the indexed look-up, it is nonetheless ROM-able and will can be be located in Flash memory which on most MCUs is a less scarce resource than SRAM. Being static const the linker/compiler will most likely place the tables in ROM without any specific directive - though you should check the link map or the toolchain documentation n this.
In both cases I have defined the command functions and executer as returning an int. That is optional of course, but you might use that for returning responses to the PC issuing the serial command.
What you are talking about are remote procedure calls. So you need to have some mechanism to serialize and un-serialize the calls.
As mentioned in the comments you can make a map from cmd to the function implementing the command. Or simply an array. But the problem remains that different functions will want different arguments.
So my suggestion would be to add a wrapper function using vardiac templates.
Prefix every command with the length of data for the command so the receiver can read a block of data for the command and knows when to dispatch it to a function. The wrapper then takes the block of data, splits it into the right size for each argument and converts it and then calls the read function.
Now you can make a map or array of those wrapper function, each one bound to one command and the compiler will generate the un-serialize code for you from the types. (You still have to do it once for each type, the compiler only combines those for the full function call).
When running the following C ++ script I obtain the necessary data to work in Gnuplot.
#include "stdio.h"
double TT;/* Imaginary part of T matrix */
double x;/* Real part of Energy */
double y;/* Imaginary part of Energy */
double G;/*Resonance Peak width */
double ER;/* Resonance Peak Energy */
main()
{
G = 120.;
ER=180.;
for(x=150.;x<=200.;x=x+1.){
for(y=-70.;y<=-50.;y=y+0.5){
/* Plotting T as a function of x and y */
TT = (G/2.)*(y+(G/2.))/(ER*ER - 2.*ER*x + G*y + (G*G/4.) + x*x + y*y);
printf("%g %2.1f %f\n", x,y,TT); /*Printing out T to standard output*/
}
printf("\n"); /* printing a newline after every column(this part is crutial) */
}
}
from the command Prompt I run the following command line:
.\3D_Data start notepad++ > mytextfile.dat
When calling the point DAT file in Gnuplot
I get the following Alert:
gnuplot> splot 'mytextfile.dat'
warning: Bad data on line 1 of file mytextfile.dat
Not enough input columns
If the content of the file is copied and inserted into a new file within Notepad ++ and saved with extension Dat; then it runs correctly in Gnuplot
I have tried with these command lines without any results
// C:\projects\helloworld> .\3D_Data ./a.out > 3Ddata
// PS C:\projects\helloworld> .\3D_Data > result.txt
// PS C:\projects\helloworld> .\3D_Data | Out-File -FilePath .\OUTPUT.txt
// PS C:\projects\helloworld> .\3D_Data test.exe > myoutput.txt
// PS C:\projects\helloworld> .\3D_Data copy > SN.txt
// PS C:\projects\helloworld> .\3D_Data echo > filename2.txt
// C:\projects\helloworld> .\3D_Data /S > Myfile.dat
something I have noticed is that the file created from Notepad ++ is almost twice small in size.
Is it possible to get a file from the CMD that is accepted by Gnuplot functions?
G N U P L O T
Version 5.4 patchlevel 1 last modified 2020-12-01
Well, the code you use looks more like C and not C++.
(which is fine too)
In C, you can use fprinf instead of printf .. have a look here:
#include <stdio.h>
int main () {
FILE *file;
const double G = 120.;
const double ER=180.;
file = fopen("file.dat","w+");//create or overwrites a file
for(int x=150; x<=200; x++){
for(double y=-70.; y<=-50.; y += 0.5){
TT = (G/2.)*(y+(G/2.))/(ER*ER - 2.*ER*x + G*y + (G*G/4.) + x*x + y*y);
fprintf(file,"%g %2.1f %f\n", x, y, TT); //writes into a file
}
fprintf(file,"\n");//same here
}
fclose(file);//flushes the file and closes it
return(0);
}
In C++, you can use fstream instead of printf .. have a look here:
#include <fstream>
#include <format>
int main () {
std::ofstream file("file.dat", std::ofstream::out | std::ofstream::trunc);
const double G = 120.;
const double ER = 180.;
for(int x=150; x<=200; x++){
for(double y=-70.; y<=-50.; y += 0.5){
TT = (G/2.)*(y+(G/2.))/(ER*ER - 2.*ER*x + G*y + (G*G/4.) + x*x + y*y);
file << x << " " << std::format("{:2.1f}",y) << TT << "\n"; //writes into a file // EDIT: I added the C++20 std::format
}
file << std::endl;//same here
}
file.close();//flushes the file and closes it
return(0);
}
Some tipps:
Ty to fix all warnings (you should have had some)
Try to do proper spaces and indentations!
make variable names readable
try to make source code that a human can read (without comments if possible)
try to make functions that express your calculations
EDIT: I changed the command int the C++ example to std::format(..), as I had an error in the code.
TT(x,y) = (G / 2.) * (y + (G / 2.)) / (ER * ER - 2. * ER * x + G * y + (G * G / 4.) + x * x + y * y)
ER=180.
G = 120.
set table 'test3D.dat'
splot [150:200] [-70:-50.1] TT(x,y)
unset table
splot 'test3D.dat' with lines
I am writing a C++ program involving boost::program_options, here I came across some problems. Some of my codes are given here.
int main(int argc, char* argv[]) {
options_description desc("useage: filterfq", options_description::m_default_line_length * 2, options_description::m_default_line_length);
options_description generic("Gerneric options", options_description::m_default_line_length * 2, options_description::m_default_line_length);
generic.add_options()
("help,h", "produce help message")
;
options_description param("Parameters", options_description::m_default_line_length * 2, options_description::m_default_line_length);
param.add_options()
("checkQualitySystem,c", bool_switch(), "only check quality system of the fastq file")
("baseNrate,N", value<float>() -> default_value(0.05), "maximum rate of \'N\' base allowed along a read")
("averageQuality,Q", value<float>() -> default_value(0), "minimum average quality allowed along a read")
("perBaseQuality,q", value<int>() -> default_value(5), "minimum quality per base allowed along a read")
("lowQualityRate,r", value<float>() -> default_value(0.5), "maximum low quality rate along a read")
("rawQualitySystem,s", value<int>(), "specify quality system of raw fastq\n0: Sanger\n1: Solexa\n2: Illumina 1.3+\n3: Illumina 1.5+\n4: Illumina 1.8+")
("preferSpecifiedRawQualitySystem,p", bool_switch(), "indicate that user prefers the given quality system to process")
;
options_description input("Input", options_description::m_default_line_length * 2, options_description::m_default_line_length);
input.add_options()
("rawFastq,f", value< vector<path> >() -> required() -> multitoken(), "raw fastq file(s) that need cleaned, required")
;
options_description output("Output", options_description::m_default_line_length * 2, options_description::m_default_line_length);
output.add_options()
("cleanQualitySystem,S", value<int>() -> default_value(4), "specify quality system of cleaned fastq, the same as rawQualitySystem")
("outDir,O", value<path>() -> default_value(current_path()), "specify output directory, not used if cleanFastq is specified")
("outBasename,o", value<string>(), "specify the basename for output file(s), required if outDir is specified")
("cleanFastq,F", value< vector<path> >() -> multitoken(), "cleaned fastq file name(s), not used if outDir or outBasename is specified")
("droppedFastq,D", value< vector<path> >() -> multitoken(), "fastq file(s) containing reads that are filtered out")
;
desc.add(generic).add(param).add(input).add(output);
variables_map vm;
store(command_line_parser(argc, argv).options(desc).run(), vm);
if (vm.count("help")) {
cout << desc << "\n";
return 0;
}
...
}
The #include using namespace parts are not given here. When I typed the command to see the help message, it showed me the following
useage: filterfq:
Gerneric options:
-h [ --help ] produce help message
Parameters:
-c [ --checkQualitySystem ] only check quality system of the fastq file
-N [ --baseNrate ] arg (=0.0500000007) maximum rate of 'N' base allowed along a read
-Q [ --averageQuality ] arg (=0) minimum average quality allowed along a read
-q [ --perBaseQuality ] arg (=5) minimum quality per base allowed along a read
-r [ --lowQualityRate ] arg (=0.5) maximum low quality rate along a read
-s [ --rawQualitySystem ] arg specify quality system of raw fastq
0: Sanger
1: Solexa
2: Illumina 1.3+
3: Illumina 1.5+
4: Illumina 1.8+
-p [ --preferSpecifiedRawQualitySystem ] indicate that user prefers the given quality system to process
Input:
-f [ --rawFastq ] arg raw fastq file(s) that need cleaned, required
Output:
-S [ --cleanQualitySystem ] arg (=4) specify quality system of cleaned fastq, the same as rawQualitySystem
-O [ --outDir ] arg (="/home/tanbowen/filterfq") specify output directory, not used if cleanFastq is specified
-o [ --outBasename ] arg specify the basename for output file(s), required if outDir is specified
-F [ --cleanFastq ] arg cleaned fastq file name(s), not used if outDir or outBasename is specified
-D [ --droppedFastq ] arg fastq file(s) containing reads that are filtered out
The help message looks somewhat ugly, especially the "0.0500000007", I want to improve it. But I googled for a long time, I cannot find solutions. So I ask for help here solve following problems:
Is there any method to reformat the help message?
If 1 is not possible, how to drop out the part of "arg" and default values?
And is there any way to align the right-hand descriptions?
One extra question: how can I prevent the following command to be executed
filter -f <some file> -f <some file>
i.e., do not allow the same option to be specified more than once?
Thanks very much!!
Yes, see below (shows the general form of collecting and displaying formatted options)
look at the constructor of options_description. It allows you to specify column widths.
Here is a (real) example of a custom option value. In my case I wanted to collect a buffer size in bytes, but also wanted to be able to parse things like 4K or 1M.
struct bytesize_option
{
bytesize_option(std::size_t val = 0) : _size(val) {}
std::size_t value() const { return _size; }
void set(std::size_t val) { _size = val; }
private:
std::size_t _size;
};
std::ostream& operator<<(std::ostream& os, bytesize_option const& hs);
std::istream& operator>>(std::istream& is, bytesize_option& hs);
namespace {
static constexpr auto G = std::size_t(1024 * 1024 * 1024);
static constexpr auto M = std::size_t(1024 * 1024);
static constexpr auto K = std::size_t(1024);
}
std::ostream& operator<<(std::ostream& os, bytesize_option const& hs)
{
auto v = hs.value();
if (v % G == 0) { return os << (v / G) << 'G'; }
if (v % M == 0) { return os << (v / M) << 'M'; }
if (v % K == 0) { return os << (v / K) << 'K'; }
return os << v;
}
std::istream& operator>>(std::istream& is, bytesize_option& hs)
{
std::string s;
is >> s;
static const std::regex re(R"regex((\d+)([GMKgmk]){0,1})regex");
std::smatch match;
auto matched = std::regex_match(s, match, re);
if(!matched) {
throw po::validation_error(po::validation_error::invalid_option_value);
}
if (match[2].matched)
{
switch (match[2].str().at(0))
{
case 'G':
case 'g':
hs.set(std::stoul(match[1].str()) * G);
break;
case 'M':
case 'm':
hs.set(std::stoul(match[1].str()) * M);
break;
case 'K':
case 'k':
hs.set(std::stoul(match[1].str()) * K);
break;
}
}
else {
hs.set(std::stoul(match[1].str()));
}
return is;
}
You would use it like so:
return boost::shared_ptr<po::option_description> {
new po::option_description("server.max-header-size,x",
po::value(&_max_hdr_size)
->default_value(_max_hdr_size),
"The maximum size (in bytes) of a HTTP header "
"that the server will accept")
};
Where in this case, _max_hdr_size is defined:
bytesize_option _max_hdr_size;