I am using the CLI11 library (link) for parsing of command line arguments of my programm.
Now I want to print information about the options of my program to stdout.
It seems that flags added via App::add_flag(...) are stored as Options internally as well, but I need to distinguish them in my output.
How can I determine which option is a flag?
Here is a simplified example:
std::string file, bool myflag;
CLI::App *command = app.add_subcommand("start", "Start the program");
command->add_option("file", file, "This is a file option")->required();
command->add_flag("--myflag", myflag);
print_description(command);
...
std::string print_description(CLI::App* command) {
for (const auto &option : command->get_options()) {
result << R"(<option name=")" << option->get_name() << R"(" description=")" << option->get_description()
<< R"(" type=")";
if (/*option is a flag*/) {
result << "flag";
} else {
result << "option";
}
result << R"("/>)";
}
return result.str();
}
According to this issue: https://github.com/CLIUtils/CLI11/issues/439, the function Option::get_expected_min will always return 0 for flags.
So it's possible to check it like this:
if (option->get_expected_min() == 0) {
result << "flag";
} else {
result << "option";
}
Related
I am using a boost::shared_ptr to point to a plugin class. Plugin is a map <string, shared_ptr>. The first time I find a certain plugin in the map, it works fine. However, any subsequent time I try to find a particular plugin, I get a SIGSEGV error. When stepping through my code, I get to foundPlugin = a->second->onCommand(command);and find that a->second is not accessible anymore. This error only happens when I am running in Linux, however. I have no issues while running in Windows. Is there some sort of issue with boost::shared_ptr and linux? I have tried using std::shared_ptr, but I have to use a boost::dll::import function that returns a boost::shared_ptr, and I haven't found an alternative for that yet. Any insight is greatly appreciated!
I load plugins like this:
bool PluginManager::loadPlugin(std::string pluginPath, std::string
pluginName, std::string pluginType)
{
bool couldLoad = false;
boost::filesystem::path libPath = boost::filesystem::current_path();
boost::shared_ptr<my_plugin_api> plugin;
std::cout << "Loading the plugin " << pluginName << std::endl;
if (pluginName == "")
{
pluginName = "plugName";
}
try
{
plugin = boost::dll::import<my_plugin_api>(
libPath / pluginName,
pluginType,
dll::load_mode::append_decorations
);
Plugin.insert(std::pair<std::string,boost::shared_ptr<my_plugin_api>>
(pluginName, plugin));
std::cout << "Loading the plugin " << pluginName << " (SUCCESS)" <<
std::endl;
couldLoad = true;
}
catch (const std::exception& e)
{
std::cerr << e.what() << std::endl;
}
return couldLoad;
}
After much more testing, I feel like my problems are in the above section of code. the boost::dll::import function acts as if it finds a .so, but does not return anything in the boost::shared_ptr, which in turn causes the second snippet of code to fail. Any ideas of why this boost::dll::import function might be acting weirdly in Linux?
bool PluginManager::onCommand(const char* command, const char* pluginName)
{
bool foundPlugin = false;
auto a = Plugin.find(pluginName);
if (a == Plugin.end())
{
std::cerr << "plugin " << pluginName << " not found" << std::endl;
}
else
{
foundPlugin = a->second->onCommand(command);
}
return foundPlugin;
}
So I am running a program that has the users input a command. I have a text file with a list of acceptable command words. I open the file first to make sure that what the user input is indeed a valid command, but after that step I close the file. After that, I use some if/else statements to run a function corresponding to the command. One command is to list all of the valid commands, which requires me to open the file again and simply output the contents. At this step, however, the program simply outputs nothing. Here is the code:
#include<iostream>
#include<string>
#include<fstream>
using namespace std;
void mainMenu();
void comJunc(string comString);
void comList();
bool isCommand(string comString);
int main()
{
mainMenu();
return 0;
}
void mainMenu()//This function inputs a command
{
string comString;
bool isCom = false;
cout << endl;
cout << "||Welcome to the main menu||\n\nPlease enter a command. For a list of commands type \"comlist\"\n";
do
{
cout << ">";
cin >> comString;//Input command "comlist"
isCom = isCommand(comString);//Checks if command is valid
if(isCom == false)
cout << endl << "**Error** Command \"" << comString << "\" was not found. Type \"comlist\" for help\n";
}while(isCom == false);
comJunc(comString);
return;
}
bool isCommand(string comString)
{
bool sFlag = false;
string inString;
ifstream comFile;
if(comFile.is_open() == false)
{
comFile.open("commands.txt");
}
while(getline(comFile, inString))
{
if(comString == inString)
sFlag = true;
}
comFile.close();
return sFlag;
}
void comJunc(string comString)//Executes function based on command
{
//comlist, continue, start, customize, exit
if(comString == "comlist")
{
comList();//Calles comList function
}
else if(comString == "exit")
{
//does nothing (exits)
}
else
cout << "Error in comJunc";
return;
}
void comList()
{
string inString;
ifstream comFile;
if(comFile.is_open() == false)
{
comFile.open("command.txt");
}
while(getline(comFile, inString))//This loop is not executed
{
cout << "Random text";//Text is not displayed
cout << endl << inString;
}
cout << endl;
comFile.close();
return;
}
The console looks like this:
||Welcome to the main menu||
Please enter a command. For a list of commands type "comlist"
>
At which point I type "comlist"
The desired result is for the command.txt file to output its contents. It should look like this:
comlist
command2
command3
command4
command5
Instead, the screen is left blank and the program exits as usual.
Any help would be greatly appreciated. Thanks.
I have wrote following code to write logs in my log file.
This code is working fine for logging messages but now i have to integrate this in multiple files i need file path of caller, caller function name and line number.
Kindly help me to achieve this .
#include "Source.h"
bool CLogManager::fileOpenError = false;
std::string CLogManager::logFileName = "";
CLogManager* CLogManager::logManager = NULL;
FILE* CLogManager::file = NULL;
CLogManager :: CLogManager(){}
CLogManager :: ~CLogManager()
{
if (file)
fclose(file);
}
CLogManager* CLogManager::getInstance()
{
if(logManager==NULL)
{
logManager = new CLogManager();
logFileName = currentDateTime();
}
return logManager;
}
const std::string CLogManager::currentDateTime()
{
time_t now = time(0);
char currTime[30];
strftime(currTime, sizeof(currTime), "Log_%Y_%m_%dT%H_%M_%S.xml", localtime(&now));
return currTime;
}
void CLogManager::Log (char *message)
{
file = fopen(logFileName.c_str(), "a+");
if(file == NULL)
{
if(fileOpenError == false)
{
std::cout << "There was an error While opening Log File."<<std::endl;
fileOpenError = true;
}
return;
}
fputs(message, file);
fputs("\n", file);
}
int main ()
{
CLogManager::getInstance();
CLogManager::Log("Sorry some error occured");
CLogManager::Log("Please try again");
CLogManager::Log("Wait");
return 0;
}
Since C++ 20, you can use std::source_location.
From the example on CPPReference.com:
#include <iostream>
#include <string_view>
#include <source_location>
void log(const std::string_view message,
const std::source_location location =
std::source_location::current())
{
std::cout << "file: "
<< location.file_name() << "("
<< location.line() << ":"
<< location.column() << ") `"
<< location.function_name() << "`: "
<< message << '\n';
}
template <typename T> void fun(T x)
{
log(x);
}
int main(int, char*[])
{
log("Hello world!");
fun("Hello C++20!");
}
Output:
file: main.cpp(23:8) `int main(int, char**)`: Hello world!
file: main.cpp(18:8) `void fun(T) [with T = const char*]`: Hello C++20!
When I need a fast "printf" logging, I use this marco for message logging that is branded with filename and line:
#define _MSG(msg) do{ std::cerr << __FILE__ << "(#" << __LINE__ << "): " << msg << '\n'; } while( false )
The above macro will inject a msg into a std::cerr pipeline. You can take out parts you need or modify it for your purposes. It hinges on __FILE__ and __LINE__ macros, which are defined by standard:
__FILE__
The presumed name of the current source file (a character string literal).
__LINE__
The presumed line number (within the current source file) of the current source line (an integer
constant).
Function names are not so easy to get, and I don't think there is a nice way to get it.
If you want logging through functions I would define some macro, or make function that would take int and char* for line and file respectively. Something like log(int line, char* source_file, string message).
To utilize the LogManager class that you already have written, you could do something like this:
void CLogManager::Log(char *message, std::string FileName = "Unset", std::string FunctionName = "Unset", int LineNumber = -1)
{
...
}
Then, anywhere you want to use your Logging function as it is right now, you would just do:
::Log(message);
But, if you wanted to include File/Function/Line information, you would do this:
::Log(message, __FILE__, __FUNCTION__, __LINE__);
You can adjust the defaults from "Unset" to anything you wanted (including just ""). I might also suggest in that function that you could have the output be different based upon whether the FileName parameter (passed to the function) is the default or not. That way your log file would look clean.
So, quick question:
I am tasked with making a program that reads in a file, does some fancy things, and writes out to an html file with the re-purposed text, all simple stuff.
Furthermore the program must be able to accept up to four command line arguments (but a minumum of two). The executable of course, the file it is reading in, the file name it will be reading out to, and finally a "-r" argument for more information on the file (the amount of paragraphs, etc).
So the question I have is as follows:
the "-r" argument can be anywhere in the arguments (as long as it comes after argv[0] of course), or it can be completely non-existent (as can the output file name).
This is still simple to do, a little tedious writing a bunch of if's or even a switch or two, but I can't help but think that there may be an easier way to accomplish this, rather than having a plethora of if statements.
Any help would be greatly appreciated. (I also suppose you don't need any code considering I don't exactly have a problem.)
Here is roughly how one might do it without a library:
GCC 4.8.2: g++ -Wall -Wextra -std=c++0x main.cpp
#include <iostream>
#include <string>
int main(int argc, char* argv[]) {
char* input = nullptr;
char* output = nullptr;
char* r_arg = nullptr;
--argc; ++argv; // Skip the program name.
const char* def_in = "default in";
const char* def_out = "default out";
const char* def_r = "default r";
while (0 < argc) {
if (std::string(argv[0]) == "-r") {
// This code requires a space after the "-r", which is unusual.
++argv;
--argc;
r_arg = argv[0]; }
else if (input == nullptr) {
input = argv[0]; }
else if (output == nullptr) {
output = argv[0]; }
else {
std::cerr << "error: unexpected arg '" << argv[0] << "'\n"; }
++argv;
--argc; }
if (input == nullptr) {
input = const_cast<char*>(def_in); }
if (output == nullptr) {
output = const_cast<char*>(def_out); }
if (r_arg == nullptr) {
r_arg = const_cast<char*>(def_r); }
std::cout << "input: " << input << "\n"
<< "output: " << output << "\n"
<< "r arg: " << r_arg << "\n";
return 0; }
I added some commands to produce an output file in methods of a given class. This worked perfectly well, and output file was produced during execution. Now that I made major changes to the code (but not at all to the output file commands), I am not producing output correctly anymore? Where can this come from? The code I changed seems not have any connexion to output commands. Constructor looks like
solverMethod::solverMethod(solverInput*inp_):solverMethod(inp_)
{
ndim = m_input->getNbParams();
bestFuncEval = DBL_MAX;
NMAX = m_input->getMaxIter();
FTOL = m_input->getTolerance();
NITER = 0;
logMode = true;
osOutput.open("F://Output.txt") ;
}
where member boolean logMode decide whether comments are active or not.
In different methods, I have code like
if(logMode)
{
osOutput << "\n";
osOutput << " - - BUILD ";
osOutput << "\n";
osOutput << "INITIAL";
osOutput << "\n";
for(int k=0;k<npts;k++)
{
for(int j=0;j<m_ndim;j++)
{
osOutput << s_[k][j] ;
osOutput << ", ";
}
}
osOutput << "\n";
}
to produce comments
and then I have at the end of major method:
// ....
osOutput.close();
return true;
}
Do you have any further info
What is the status of the stream after the open? You almost always
want to check whether the open succeeded (and whether all of the
writes succeeded after the close). You might try something like:
osOutput.open(...);
if ( !osOutput.is_open() ) {
std::cerr << "Cannot create ..., error was: " << strerror( errno);
}
This will give you more information about what is wrong.