This code fragment from 2009:
#include "../asklib.hxx"
std::vector<AskBase*> questions;
AskUI<std::vector<AskBase*> > ui(questions);
ui.add(new Ask<std::string>("Enter your name: ", 3, 25));
ui.add(new Ask<std::string>("Enter your city: ", 2, 25));
ui.add(new Ask<std::string>("Enter your state: ", 2, 2));
ui.add(new Ask<int>("Enter your age: ", 18, 150));
was modified to take advantage of C++14 features in the initialization of questions
:
#include "../asklib.hxx"
// Define user interface.
std::vector<AskBase*> questions {
new Ask<std::string>{"Enter your name: ", 3, 25},
new Ask<std::string>{"Enter your city: ", 2, 25},
new Ask<std::string>{"Enter your state: ", 2, 2},
new Ask<int> {"Enter your age: ", 18, 120}};
AskUI<std::vector<AskBase*>> ui {questions};
int main(){
ui.run();
if (ui.completed())
for (auto ii: ui.items())
std::cout << ii->id() << ii->l_answer_string() << std::endl;
}
As questions
is not directly used in the routine later it seems like there should be a way to eliminate the variable by just passing the std::vector<AskBase*>
directly the AskUI
constructor. Are there suggestions for removing it or rational as to the necessity of it staying and any other comments? I'm looking for C++14 and/or C++1z answers.
The code review aspect is requested for the main file specifically for making the library easier to use. The contents of asklib.hxx
and asklib.cxx
follow thought other than regarding the improvements needed to support the user of the library it is not presented for the review request.
// asklib.hxx
#ifndef ASKLIB_HXX
#define ASKLIB_HXX
#include <vector>
#include <libintl.h>
#include <locale.h>
#include <string>
#include <iostream>
#include <sstream>
#include <boost/format.hpp>
#define gettext_noop(S) S
// ============================================================================
class AskBase {
// ============================================================================
private:
class Locale{
private:
std::string m_gtlocale;
std::string m_gtdomain;
std::string m_gtdir;
public:
Locale(const char* a_gtlocale="", const char* a_gtdomain="asklib", const char* a_gtdir="./exp/po")
:m_gtlocale(a_gtlocale), m_gtdomain(a_gtdomain), m_gtdir(a_gtdir){setLanguage("pl_PL.utf8");}
// ----------------------------------------------------------------------------
void setLanguage(std::string a_language)
// ----------------------------------------------------------------------------
{
m_gtlocale = a_language;
setlocale(LC_ALL, m_gtlocale.c_str());
bindtextdomain(m_gtdomain.c_str(), m_gtdir.c_str());
textdomain(m_gtdomain.c_str());
}
};
struct Status {
static auto const c_ROOT =-999;
static auto const c_PREVIOUS = -1;
static auto const c_NEXT = 1;
static auto const c_EOF = 999;
};
static int c_instance;
static const char* c_e_REQ_EOF;
static const char* c_e_REQ_ROOT;
static const char* c_e_REQ_PREVIOUS;
static const char* c_e_REQ_HELP;
static const char* c_e_REQ_VERSION;
static const char* c_e_REQ_LANGUAGE;
static const char* c_e_MSG_HELP;
static auto const c_MAX_TRIES = 2;
private:
decltype(AskBase::Status::c_ROOT) ask_user();
template<typename C>friend class AskUI;
virtual bool validate(std::string a_l_response) = 0;
int m_instance_id;
protected:
std::string m_e_raw_prompt;
std::string m_l_answer_string;
public:
Locale c_locale;
AskBase(const char* a_e_prompt);
void changeLaguage(std::string a_language){c_locale.setLanguage(a_language);}
std::string l_prompt(){return gettext(m_e_raw_prompt.c_str());}
std::string l_answer_string(){return m_l_answer_string;}
auto id(){return m_instance_id;}
};
// ============================================================================
template<typename T> // ex: int, std::string
class Ask : public AskBase{
// ============================================================================
private:
int m_min;
int m_max;
bool validate(std::string a_l_response);
public:
Ask(const char* a_e_raw_prompt, int a_min, int a_max)
:AskBase{a_e_raw_prompt}, m_min{a_min}, m_max{a_max}{}
};
// ============================================================================
template<typename Container>
class AskUI {
// ============================================================================
private:
int status = AskBase::Status::c_ROOT;
public:
Container& m_asks;
AskUI(Container& a_asks, const char* a_gtlocale="")
:m_asks{a_asks}{}
void add(AskBase* a_ask){m_asks.push_back(a_ask);}
bool completed(){return AskBase::Status::c_NEXT==status;};
Container& items(){return m_asks;};
// ----------------------------------------------------------------------------
auto run()
// ----------------------------------------------------------------------------
{
for (typename Container::iterator ii=m_asks.begin();
ii!=m_asks.end();
ii=(AskBase::Status::c_ROOT==status)? m_asks.begin():
(AskBase::Status::c_PREVIOUS==status)?(m_asks.begin()==ii?m_asks.begin():ii-1):
(AskBase::Status::c_NEXT==status)?ii+1:
m_asks.end())
status = (*ii)->ask_user();
return;
}
};
#endif
// asklib.cxx
#include "asklib.hxx"
// Input string the user enters to request program to exit.
const char* AskBase::c_e_REQ_EOF = gettext_noop("^Z");
// Input string the user enters to request control to go to the first prompt in the input tree.
const char* AskBase::c_e_REQ_ROOT = gettext_noop("^^");
// Input string the user enters to request control to go to the previous prompt.
const char* AskBase::c_e_REQ_PREVIOUS = gettext_noop("^");
// Input string the user enters to request version info to be displayed.
const char* AskBase::c_e_REQ_VERSION = gettext_noop("?v");
// Input string the user enters to request a new language.
const char* AskBase::c_e_REQ_LANGUAGE = gettext_noop("^:");
// Input string the user enters to request help info to be displayed.
const char* AskBase::c_e_REQ_HELP = gettext_noop("?");
// Help displays useful info for user, Version displays version of software, the remainder are navigation controls: Root
// directs control to the first prompt in the input tree, Previous directs control goes to previous input prompt, Exit
// terminates the program.
const char* AskBase::c_e_MSG_HELP = gettext_noop(" %1$2s - Help\n %2$2s - Version\n %3$2s - Language\n %4$2s - Root\n %5$2s - Previous\n %6$2s - Exit\n");
int AskBase::c_instance = 0;
// ----------------------------------------------------------------------------
AskBase::AskBase(const char* a_e_prompt)
:m_e_raw_prompt(a_e_prompt), m_instance_id(++c_instance)
// ----------------------------------------------------------------------------
{
if (1 == c_instance){
c_locale.setLanguage("");
}
}
// ----------------------------------------------------------------------------
decltype(AskBase::Status::c_NEXT) AskBase::ask_user()
// ----------------------------------------------------------------------------
{
for (auto tries_left = c_MAX_TRIES; tries_left > 0;)
{
std::cout << gettext(m_e_raw_prompt.c_str());
std::string l_response;
getline(std::cin, l_response);
if (gettext(c_e_REQ_EOF) == l_response || std::cin.eof() || std::cin.bad())
return Status::c_EOF;
else if (gettext(c_e_REQ_ROOT) == l_response)
return Status::c_ROOT;
else if (gettext(c_e_REQ_PREVIOUS) == l_response)
return Status::c_PREVIOUS;
else if (gettext(c_e_REQ_VERSION) == l_response)
std::cout << SSVID_ICON << SSVID << std::endl;
else if (gettext(c_e_REQ_LANGUAGE) == l_response){
Ask<std::string> lang{"Enter new language: ", 2, 10};
lang.ask_user();
std::cout << "New language is: " << lang.l_answer_string() << std::endl;
c_locale.setLanguage(lang.l_answer_string());
}
else if (gettext(c_e_REQ_HELP) == l_response)
std::cout << boost::format(gettext(c_e_MSG_HELP))
% gettext(c_e_REQ_HELP) % gettext(c_e_REQ_VERSION) % gettext(c_e_REQ_LANGUAGE) % gettext(c_e_REQ_ROOT) % gettext(c_e_REQ_PREVIOUS) % gettext(c_e_REQ_EOF);
else if (validate(l_response)){
m_l_answer_string = l_response;
return Status::c_NEXT;
}
else
--tries_left;
}
return Status::c_EOF;
}
// ----------------------------------------------------------------------------
template<>
bool Ask<std::string>::validate(std::string a_l_response)
// ----------------------------------------------------------------------------
{
if (a_l_response.length() < m_min)
std::cout
<< boost::format(gettext("Invalid input. "))
<< boost::format(ngettext("Enter a string at least %d character long. ", "Enter a string at least %d characters long. ", m_min)) % m_min
<< std::endl;
else if (a_l_response.length() > m_max)
std::cout
<< boost::format(gettext("Invalid input. "))
<< boost::format(ngettext("Enter a string at most %d character long. ", "Enter a string at most %d characters long. ", m_max)) % m_max
<< std::endl;
else
return true;
return false;
}
// ----------------------------------------------------------------------------
template<>
bool Ask<int>::validate(std::string a_l_response)
// ----------------------------------------------------------------------------
{
int intAnswer;
if (!(std::stringstream(a_l_response) >> intAnswer))
std::cout
<< gettext("Invalid input. ")
<< gettext("Enter an integer.")
<< std::endl;
else if (intAnswer < m_min)
std::cout
<< boost::format(gettext("Invalid input. "))
<< boost::format(gettext("Enter at least %d. ")) % m_min
<< std::endl;
else if (intAnswer > m_max)
std::cout
<< boost::format(gettext("Invalid input. "))
<< boost::format(gettext("Enter at most %d. ")) % m_max
<< std::endl;
else
return true;
return false;
}
AskBase*
s instd::unique_ptr<AskBase>
. Raw pointers should never own anything. – David Feb 24 at 2:35