Perfectly forwarding function wrapper? - c++17

We consider the following functor, to help to detect what version of of its operator() is called.
struct functor
{
void operator()() {std::cout << "functor::operator" << std::endl;}
void operator()() const {std::cout << "functor::operator const" << std::endl;}
void operator()() volatile {std::cout << "functor::operator volatile" << std::endl;}
void operator()() const volatile {std::cout << "functor::operator const volatile" << std::endl;}
void operator()(int) & {std::cout << "functor::operator &" << std::endl;}
void operator()(int) const& {std::cout << "functor::operator const&" << std::endl;}
void operator()(int) volatile& {std::cout << "functor::operator volatile&" << std::endl;}
void operator()(int) const volatile& {std::cout << "functor::operator const volatile&" << std::endl;}
void operator()(int) && {std::cout << "functor::operator &&" << std::endl;}
void operator()(int) const&& {std::cout << "functor::operator const&&" << std::endl;}
void operator()(int) volatile&& {std::cout << "functor::operator volatile&&" << std::endl;}
void operator()(int) const volatile&& {std::cout << "const volatile&&" << std::endl;}
};
I would like to know, in C++17 (if doable of course) how to write two functor wrappers storing the function in a tuple and using class template argument deduction:
The first one wrapper1 that will call the operator depending on the qualifiers of the functor
The second one wrapper2 that will call the operator depending on the qualifiers of the wrapper
For example:
wrapper1 w1(functor{});
wrapper2 w2(functor{});
w1(0); // should call the && version since the functor was built as a temporary
w2(0); // should call the & version since the wrapper was built as a value
Here are some very rough exploration of the kind of things I am looking for. Note: this is just to give a "taste" of what I am thinking of.
template <class F>
struct wrapper_a
{
constexpr wrapper_a(F f) noexcept: _tuple(f) {}
template <class... Args>
void operator()(Args&&... args)
{std::get<0>(_tuple)(std::forward<Args>(args)...);}
std::tuple<F> _tuple;
};
template <class F>
struct wrapper_b
{
template <class G>
constexpr wrapper_b(G&& g) noexcept: _tuple(std::forward<G>(g)) {}
template <class... Args>
void operator()(Args&&... args)
{std::get<0>(_tuple)(std::forward<Args>(args)...);}
std::tuple<F> _tuple;
};
template <class F> wrapper_b(F&& f) -> wrapper_b<F>;
template <class F>
struct wrapper_c
{
template <class G>
constexpr wrapper_c(G&& g) noexcept: _tuple(std::forward<G>(g)) {}
template <class... Args>
void operator()(Args&&... args)
{std::forward<F>(std::get<0>(_tuple))(std::forward<Args>(args)...);}
std::tuple<F> _tuple;
};
template <class F> wrapper_c(F&& f) -> wrapper_c<F>;
How to achieve what I am looking for? Is that even possible?

Not sure to understand your exactly requirements... and I'm a rookie with reference forwarding... anyway
The first one wrapper1 that will call the operator depending on the qualifiers of the functor
Maybe an expert can avoid this complication but seems to me that you need two template parameters
template <typename F, typename G>
struct wrapper1
where F is the type of the copy of the parameter passed to the constructor and G is the intended type.
So you have in it a F value
F f;
and you can use it trough forwarding
template <typename ... Args>
void operator() (Args && ... as) &
{ std::forward<G>(f)(std::forward<Args>(as)...); }
To simplify, you can define a template deduction as follows
template <typename F>
wrapper1(F && f) -> wrapper1<std::decay_t<F>, decltype(std::forward<F>(f))>;
The second one wrapper2 that will call the operator depending on the qualifiers of the wrapper
Maybe I'm wrong but this seems to me a littler simpler.
You need only a template parameter
template <typename F>
struct wrapper2
and you only have to use std::move() in r-reference operators
template <typename ... Args>
void operator() (Args && ... as) &&
{ std::move(f)(std::forward<Args>(as)...); }
The deduction guide is simple
template <typename F>
wrapper2(F && f) -> wrapper2<std::decay_t<F>>;
The following is a limited (to const/not-const and l-value/r-value reference alternatives) but full working examples.
Observe that there are some compilation errors caused by constness problem (if you initialize a const wrapper1 with a not-const callable, there are problems)
#include <iostream>
struct functor
{
void operator()(int) &
{std::cout << "functor::operator &" << std::endl;}
void operator()(int) const &
{std::cout << "functor::operator const &" << std::endl;}
void operator()(int) &&
{std::cout << "functor::operator &&" << std::endl;}
void operator()(int) const &&
{std::cout << "functor::operator const &&" << std::endl;}
};
template <typename F, typename G>
struct wrapper1
{
template <typename H>
constexpr wrapper1 (H && f0) noexcept : f{std::forward<H>(f0)}
{}
template <typename ... Args>
void operator() (Args && ... as) &
{ std::forward<G>(f)(std::forward<Args>(as)...); }
template <typename ... Args>
void operator() (Args && ... as) const &
{ std::forward<G>(f)(std::forward<Args>(as)...); }
template <typename ... Args>
void operator() (Args && ... as) &&
{ std::forward<G>(f)(std::forward<Args>(as)...); }
template <typename ... Args>
void operator() (Args && ... as) const &&
{ std::forward<G>(f)(std::forward<Args>(as)...); }
F f;
};
template <typename F>
wrapper1(F && f)
-> wrapper1<std::decay_t<F>, decltype(std::forward<F>(f))>;
template <typename F>
struct wrapper2
{
template <typename H>
constexpr wrapper2 (H && f0) noexcept : f{std::forward<H>(f0)}
{}
template <typename ... Args>
void operator() (Args && ... as) &
{ f(std::forward<Args>(as)...); }
template <typename ... Args>
void operator() (Args && ... as) const &
{ f(std::forward<Args>(as)...); }
template <typename ... Args>
void operator() (Args && ... as) &&
{ std::move(f)(std::forward<Args>(as)...); }
template <typename ... Args>
void operator() (Args && ... as) const &&
{ std::move(f)(std::forward<Args>(as)...); }
F f;
};
template <typename F>
wrapper2(F && f) -> wrapper2<std::decay_t<F>>;
int main ()
{
functor fc;
functor const fd;
wrapper1 w1a{functor{}};
wrapper1 w1b{static_cast<functor const &&>(functor{})};
wrapper1 w1c{fc};
wrapper1 w1d{fd};
wrapper1 const w1e{functor{}};
wrapper1 const w1f{static_cast<functor const &&>(functor{})};
wrapper1 const w1g{fc};
wrapper1 const w1h{fd};
w1a(0);
w1b(0);
w1c(0);
w1d(0);
std::cout << "----------------------------" << std::endl;
// w1e(0); // compilation error
w1f(0);
// w1g(0); // compilation error
w1h(0);
std::cout << "----------------------------" << std::endl;
wrapper1<functor, functor&&>{functor{}}(0);
wrapper1<functor, functor const &&>
{static_cast<functor const &&>(functor{})}(0);
wrapper1<functor, functor&>{fc}(0);
wrapper1<functor, functor const &>{fd}(0);
std::cout << "----------------------------" << std::endl;
// (wrapper1 <functor, functor&&> const)
//{functor{}}(0); // compilation error
(wrapper1<functor, functor const &&> const)
{static_cast<functor const &&>(functor{})}(0);
// (wrapper1<functor, functor&> const){fc}(0); // compilation error
(wrapper1<functor, functor const &> const){fd}(0);
wrapper2 w2a{functor{}};
wrapper2 w2b{static_cast<functor const &&>(functor{})};
wrapper2 w2c{fc};
wrapper2 w2d{fd};
wrapper2 const w2e{functor{}};
wrapper2 const w2f{static_cast<functor const &&>(functor{})};
wrapper2 const w2g{fc};
wrapper2 const w2h{fd};
std::cout << "----------------------------" << std::endl;
w2a(0);
w2b(0);
w2c(0);
w2d(0);
std::cout << "----------------------------" << std::endl;
w2e(0);
w2f(0);
w2g(0);
w2h(0);
std::cout << "----------------------------" << std::endl;
wrapper2<functor>{functor{}}(0);
wrapper2<functor>{static_cast<functor const &&>(functor{})}(0);
wrapper2<functor>{fc}(0);
wrapper2<functor>{fd}(0);
std::cout << "----------------------------" << std::endl;
(wrapper2<functor> const){functor{}}(0);
(wrapper2<functor> const){static_cast<functor const &&>(functor{})}(0);
(wrapper2<functor> const){fc}(0);
(wrapper2<functor> const){fd}(0);
}

Related

How to solve yacc error "... has no declared type"

I have problem on Yacc/Bison.
We use Kotlin grammar.
This is my code :
%token BOOL
%token BREAK
%token CHAR
%token CONTINUE
%token DO
%token ENUM
%token EXTERN
%token FALSE
%token FLOAT
%token FOR
%token FN
%token IN
%token INT
%token LET
%token LOOP
%token MATCH
%token MUT
%token PRINT
%token PRINTLN
%token PUB
%token RETURN
%token SELF
%token STATIC
%token STR
%token STRUCT
%token TRUE
%token USE
%token WHERE
%token IF
%token ELSE
%token <Token> INTEGER
%token <Token> STRING
%token <Token> REAL
%token <Token> ID
%type <Token> arrDec type expression integerExpr realExpr boolExpr stringExpr functionInvoc
varDec: LET MUT ID ':' type ';' {
Trace("Reducing to varDec Form LET MUT ID ':' type ';'\n");
variableEntry ve = ve_basic_notInit($3.stringVal, $5.tokenType, false);
if (symTabs.isNowGlobal())
{
ve.isGlobal = true;
printTabs();
if (ve.type== T_INT)
fp << "field static int " << ve.name << endl;
else if (ve.type == T_BOOL)
fp << "field static int " << ve.name << endl;
}
else
{
ve.isGlobal = false;
ve.stackIndex = nowStackIndex;
nowStackIndex++;
}
if (!symTabs.addVariable(ve))
yyerror("Re declaration.");
}
| LET MUT ID '=' expression ';' {
Trace("Reducing to varDec Form LET MUT ID '=' expression ';'\n");
variableEntry ve = ve_basic($3.stringVal, $5.tokenType, false);
if ($5.tokenType == T_INT)
ve.data.intVal = $5.intVal;
else if ($5.tokenType == T_FLOAT)
ve.data.floatVal = $5.floatVal;
else if ($5.tokenType == T_BOOL)
ve.data.boolVal = $5.boolVal;
else if ($5.tokenType == T_STRING)
ve.data.stringVal = $5.stringVal;
if (symTabs.isNowGlobal())
{
ve.isGlobal = true;
printTabs();
if (ve.type == T_INT)
fp << "field static int " << ve.name << " = " << ve.data.intVal << endl;
else if (ve.type == T_BOOL)
fp << "field static int " << ve.name << " = " << ve.data.boolVal << endl;
}
else
{
ve.isGlobal = false;
ve.stackIndex = nowStackIndex;
nowStackIndex++;
printTabs();
if (ve.type == T_INT)
fp << "istore " << ve.stackIndex << endl;
else if (ve.type == T_BOOL)
fp << "istore " << ve.stackIndex << endl;
}
if (!symTabs.addVariable(ve))
yyerror("Re declaration.");
}
| LET MUT ID ':' type '=' expression ';' {
Trace("Reducing to varDec Form LET MUT ID ':' type '=' expression ';'\n");
variableEntry ve = ve_basic($3.stringVal, $5.tokenType, false);
if ($5.tokenType == T_FLOAT && $7.tokenType == T_INT)
ve.data.floatVal = $7.intVal;
else if ($5.tokenType != $7.tokenType)
yyerror("expression is not equal to expression");
else if ($7.tokenType == T_INT)
ve.data.intVal = $7.intVal;
else if ($7.tokenType == T_FLOAT)
ve.data.floatVal = $7.floatVal;
else if ($7.tokenType == T_BOOL)
ve.data.boolVal = $7.boolVal;
else if ($7.tokenType == T_STRING)
ve.data.stringVal = $7.stringVal;
if (symTabs.isNowGlobal())
{
ve.isGlobal = true;
printTabs();
if (ve.type == T_INT)
fp << "field static int " << ve.name << " = " << ve.data.intVal << endl;
else if (ve.type == T_BOOL)
fp << "field static int " << ve.name << " = " << ve.data.boolVal << endl;
}
else
{
ve.isGlobal = false;
ve.stackIndex = nowStackIndex;
nowStackIndex++;
printTabs();
if (ve.type == T_INT)
fp << "istore " << ve.stackIndex << endl;
else if (ve.type == T_BOOL)
fp << "istore " << ve.stackIndex << endl;
}
if (!symTabs.addVariable(ve))
yyerror("Re declaration.");
}
| LET MUT ID ';' {
Trace("Reducing to varDec Form LET MUT ID ';'\n");
variableEntry ve = ve_basic_notInit($3.stringVal, T_INT, false);
if (symTabs.isNowGlobal())
{
ve.isGlobal = true;
printTabs();
fp << "field static int " << ve.name << endl;
}
else
{
ve.isGlobal = false;
ve.stackIndex = nowStackIndex;
nowStackIndex++;
}
if (!symTabs.addVariable(ve))
yyerror("Re declaration.");
}
;
I didn't paste them all.
The errors are here.
I don't know how to fix them.
yacc.y:188.33-34: error: symbol ID is used, but is not defined as a token and has no rules
varDec: LET MUT ID ':' type ';' {
^^
yacc.y:191.173-174: error: $3 of ‘varDec’ has no declared type
variableEntry ve = ve_basic_notInit($3.stringVal, $5.tokenType, false);
^^
yacc.y:216.165-166: error: $3 of ‘varDec’ has no declared type
variableEntry ve = ve_basic($3.stringVal, $5.tokenType, false);
^^
yacc.y:256.165-166: error: $3 of ‘varDec’ has no declared type
variableEntry ve = ve_basic($3.stringVal, $5.tokenType, false);
^^
yacc.y:300.173-174: error: $3 of ‘varDec’ has no declared type
variableEntry ve = ve_basic_notInit($3.stringVal, T_INT, false);
Please help me solve problem, thanks.

Use variadic args to call subsequent method

Assume I have function that accept 1 callable F and variadic list of arguments:
template <class F, class ...Args>
void combine(F& f, Args&& ... args)
F may have any number of arguments from 1 to sizeof...(Args). So I need using c++17 to invoke F and after that just put not used args to std::cout like that
{
std::invoke(f, [some way to restrict args]);
std::cout << [The rest of args] << "\n";
}
The great problem is detect the number of arguments of F.
As pointed by Igor Tandetnik in a comment, this isn't possible in general, because the f func can be variadic, of a lambda with a template operator(), or a instance of a class/struct with multiple operator().
Anyway... in some cases you can detect the number of the arguments with something as follows
template <typename T>
struct num_args
: public num_args<decltype(&T::operator())>
{ };
template <typename R, typename ... Args>
struct num_args<R(*)(Args...)>
: public std::integral_constant<std::size_t, sizeof...(Args)>
{ };
// other specialization follows
At this point, you can write combine() to call an helper function with an appropriate couple of indexes and a tuple of arguments
template <typename F, typename ... Args>
void combine (F & func, Args && ... as)
{
constexpr auto n1 { num_args<F>::value };
constexpr auto n2 { sizeof...(Args) - n1 };
combine_helper(std::make_index_sequence<n1>{},
std::make_index_sequence<n2>{},
func,
std::forward_as_tuple(std::forward<Args>(as)...));
}
and the helper function can be simply as follows
template <std::size_t ... Is1, std::size_t ... Is2, typename F, typename T>
void combine_helper (std::index_sequence<Is1...>,
std::index_sequence<Is2...>,
F & func, T && t)
{
func(std::get<Is1>(std::forward<T>(t))...);
(std::cout << ... << std::get<sizeof...(Is1)+Is2>(std::forward<T>(t)))
<< '\n';
}
The following is a full compiling C++17 example
#include <iostream>
#include <utility>
#include <tuple>
template <typename T>
struct num_args
: public num_args<decltype(&T::operator())>
{ };
template <typename R, typename ... Args>
struct num_args<R(*)(Args...)>
: public std::integral_constant<std::size_t, sizeof...(Args)>
{ };
template <typename R, typename ... Args>
struct num_args<R(Args...)>
: public std::integral_constant<std::size_t, sizeof...(Args)>
{ };
template <typename R, typename C, typename ... Args>
struct num_args<R(C::*)(Args...)>
: public std::integral_constant<std::size_t, sizeof...(Args)>
{ };
template <typename R, typename C, typename ... Args>
struct num_args<R(C::*)(Args...) const>
: public std::integral_constant<std::size_t, sizeof...(Args)>
{ };
template <std::size_t ... Is1, std::size_t ... Is2, typename F, typename T>
void combine_helper (std::index_sequence<Is1...>,
std::index_sequence<Is2...>,
F & func, T && t)
{
func(std::get<Is1>(std::forward<T>(t))...);
(std::cout << ... << std::get<sizeof...(Is1)+Is2>(std::forward<T>(t)))
<< '\n';
}
template <typename F, typename ... Args>
void combine (F & func, Args && ... as)
{
constexpr auto n1 { num_args<F>::value };
constexpr auto n2 { sizeof...(Args) - n1 };
combine_helper(std::make_index_sequence<n1>{},
std::make_index_sequence<n2>{},
func,
std::forward_as_tuple(std::forward<Args>(as)...));
}
void func_1 (int a, int b, int c)
{ std::cout << "the product is: " << a*b*c << '\n'; }
int main()
{
auto extra { 100 };
auto func_2 { [&](int a, int b, int c, int d)
{ std::cout << "the extra sum is: " << extra+a+b+c+d << '\n'; } };
combine(func_1, 1, 2, 3, 4, 5, 6);
combine(func_2, 1, 2, 3, 4, 5, 6);
}

bisonc++ - No production rules?

I'm trying to compile the following with bisonc++:
%baseclass-preinclude <iostream>
%lsp-needed
%token NUMBER COMMENT KEYWORD VARIABLE LOGICAND LOGICOR LOGICEQUALS DOUBLELESSER
%token DOUBLEGREATER MOD LESSER GREATER OPEN CLOSE NEGATE CURLYOPEN CURLYCLOSE SEMICOLON
%left EQUALS
%left PLUS MINUS
%left TIMES
%left DIVISION
%%
start:
expressions
{
std::cout << "start -> expressions" << std::endl;
}
;
expressions:
// empty
{
std::cout << "expressions -> epsylon" << std::endl;
}
|
exp expressions
{
std::cout << "expressions -> exp expressions" << std::endl;
}
;
exp:
NUMBER
{
std::cout << "exp -> NUMBER" << std::endl;
}
|
COMMENT
{
std::cout << "exp -> COMMENT" << std::endl;
}
|
exp LOGICAND exp
{
std::cout << "exp -> exp LOGICAND exp" << std::endl;
}
|
exp LOGICOR exp
{
std::cout << "exp -> exp LOGICOR exp" << std::endl;
}
|
exp LOGICEQUALS exp
{
std::cout << "exp -> exp LOGICEQUALS exp" << std::endl;
}
|
exp DOUBLELESSER exp
{
std::cout << "exp -> exp DOUBLELESSER exp" << std::endl;
}
|
exp DOUBLEGREATER exp
{
std::cout << "exp -> exp DOUBLEGREATER exp" << std::endl;
}
|
exp PLUS exp
{
std::cout << "exp -> exp PLUS exp" << std::endl;
}
|
exp MINUS exp
{
std::cout << "exp -> exp MINUS exp" << std::endl;
}
|
exp TIMES exp
{
std::cout << "exp -> exp EQUAL exp" << std::endl;
}
|
exp EQUAL exp
{
std::cout << "exp -> exp EQUAL exp" << std::endl;
}
|
exp DIVISION exp
{
std::cout << "exp -> exp DIVISION exp" << std::endl;
}
|
exp MOD exp
{
std::cout << "exp -> exp MOD exp" << std::endl;
}
|
exp LESSER exp
{
std::cout << "exp -> exp LESSER exp" << std::endl;
}
|
exp GREATER exp
{
std::cout << "exp -> exp GREATER exp" << std::endl;
}
|
OPEN exp CLOSE
{
std::cout << "exp -> OPEN exp CLOSE" << std::endl;
}
|
NEGATE exp
{
std::cout << "exp -> NEGATE exp" << std::endl;
}
|
CURLYOPEN exp CURLYCLOSE
{
std::cout << "exp -> CURLYOPEN exp CURLYCLOSE" << std::endl;
}
|
exp SEMICOLON
{
std::cout << "exp -> SEMICOLON" << std::endl;
}
|
KEYWORD VARIABLE SEMICOLON
{
std::cout << "exp -> KEYWORD VARIABLE SEMICOLON" << std::endl;
}
;
However, it keeps returning with the error
') encountered.1] Line 1: unrecognized input (`
': identifier or character-constant expected.
[bead.y: fatal] Line 23: No production rules
134950080
I obviously have some production rules and have no idea what I'm doing wrong. I've copied most of the code from another working example and modified it to my liking. What is wrong?
Your files are not UNIX conform (calculate.l and calculate.y)
If you're operating on Windows and you use Nodepad++, just do the following:
Edit/EOL conversion/UNIX format
Encoding/Convert your files to UTF-8 without BOM
Save and recompile with flex and bisonc++
Hope this helps
I had the same problem, and my teacher showed me, that some of the windows editors create invisible trash in your file, so edit your file with the terminal.
pico yourfile.y
Hit an Enter before the first line, than erase it.
Save
Thats it!

What is the C++ way of handling a framework to call functions of different types from a script?

I have tried different approaches and asked several specific questions regarding sub-problems of my requirement here. But my solution doesn't really work as expected, so I am taking a step back and ask here from a more general perspective. Please bear in mind that I am not a C++ pro. Not a beginner either, but I am still learning the language.
So, I have the following requirement. I need to read-in text files, which contain conditions, like "Greater" or "Equals", all like functions that return boolean values. The text files also include parameters for these conditions. Note that these parameters can be of different types (integer, decimal, etc.), and each such condition can take different number of parameters (e.g. "Equals" takes 2 paramters, while "Between" would take 3 parameters). Thus the file could look something like this:
Greater, 2, 3
Greater, 2.4, 1.0
Equals, true, true
Between, 20, 10, 30
The logic to read-in that file and parsing it is already done. Now I need to "concatenate" all these boolean functions with their parameters and check if all of them are true.
So I thought I would create functions or a class with static methods to represent these boolean test functions, then create a map of function pointers to these functions, mapped by their name. At runtime I would then read in the file, call the respective function pointer and pass-in the paramteres. That seemed easy to me, but in reality I am struggling mostly with the fact that these boolean functions can take a different number of parameters, and that these can be of different types.
Can you recommend a way to tackle that requirement in C++? I am not asking for a complete solution, but for a suitable C++ approach, or a guideline I could follow. Thanks in advance!
Here's a quick&dirty Spirit grammar for the input shown.
UPDATE
Now added invocation and implementation of the predicate functions (GreaterImpl and EqualsImpl).
I tried to be smart allowing comparisons between mixed arithmetic types (but not e.g. Greater(bool,string). If you compare incompatible types you will get a std::runtime_error exception that provides type feedback to the caller.
Live On Coliru
#include <deque>
#include <boost/spirit/include/qi.hpp>
#include <boost/fusion/adapted/struct.hpp>
namespace qi = boost::spirit::qi;
namespace Ast {
using Value = boost::variant<int, double, bool, std::string>;
using BinaryPred = std::function<bool(Value, Value)>;
using TernaryPred = std::function<bool(Value, Value, Value)>;
using Pred = boost::variant<BinaryPred, TernaryPred>;
using Values = std::vector<Value>;
struct Invocation { Pred pred; Values args; };
using Invocations = std::vector<Invocation>;
}
BOOST_FUSION_ADAPT_STRUCT(Ast::Invocation, pred, args)
namespace Predicates {
using Ast::Value;
struct Greater : boost::static_visitor<bool> {
bool operator()(Value const& a, Value const& b) const {
return boost::apply_visitor(*this, a, b);
}
template <typename T> bool operator()(T const& a, T const& b) const { return std::greater<T>{}(a, b); }
template <typename T, typename U>
typename std::enable_if<std::is_arithmetic<T>() && std::is_arithmetic<U>(), bool>::type
operator()(T const& a, U const& b) const { return a > b; }
template <typename T, typename U>
typename std::enable_if<not (std::is_arithmetic<T>() && std::is_arithmetic<U>()), bool>::type
operator()(T const&, U const&) const { throw std::runtime_error("Type Mismatch"); }
};
struct Equals : boost::static_visitor<bool> {
bool operator()(Value const& a, Value const& b) const {
return boost::apply_visitor(*this, a, b);
}
template <typename T> bool operator()(T const& a, T const& b) const { return std::equal_to<T>{}(a, b); }
template <typename T, typename U, typename enable = typename std::enable_if<std::is_arithmetic<T>() && std::is_arithmetic<U>()>::type >
bool operator()(T const& a, U const& b) const { return a == b; }
template <typename T, typename U>
typename std::enable_if<not (std::is_arithmetic<T>() && std::is_arithmetic<U>()), bool>::type
operator()(T const&, U const&) const { throw std::runtime_error("Type Mismatch"); }
};
struct Between {
bool operator()(Value const& v, Value const& lower, Value const& upper) const {
return Greater{}(v,lower) && Greater{}(upper,v);
}
};
}
static inline bool evaluate(Ast::Invocation const& i) {
struct Invoker {
using result_type = bool;
Ast::Values const& args;
result_type operator()(Ast::BinaryPred const& p) const {
if (args.size() != 2) throw std::runtime_error("Arity Mismatch");
return p(args.at(0), args.at(1));
}
result_type operator()(Ast::TernaryPred const& p) const {
if (args.size() != 3) throw std::runtime_error("Arity Mismatch");
return p(args.at(0), args.at(1), args.at(2));
}
};
return boost::apply_visitor(Invoker{i.args}, i.pred);
}
template <typename It>
struct Grammar : qi::grammar<It, Ast::Invocations()> {
Grammar() : Grammar::base_type(start) {
using namespace qi;
start = skip(blank) [ invocation % eol ];
invocation = pred >> -("," >> args);
args = arg % ",";
arg = my_double_ | qi::int_ | qi::bool_ | lexeme['"' > *~char_('"') > '"'];
}
private:
struct pred_t : qi::symbols<char, Ast::Pred> {
pred_t() {
this->add
("Greater", Predicates::Greater{})
("Equals", Predicates::Equals{})
("Between", Predicates::Between{})
;
}
} const pred;
qi::rule<It, Ast::Invocations()> start;
qi::rule<It, Ast::Invocation(), qi::blank_type> invocation;
qi::rule<It, Ast::Values(), qi::blank_type> args;
qi::rule<It, Ast::Value(), qi::blank_type> arg;
qi::real_parser<double, qi::strict_real_policies<double> > my_double_;
};
#include <sstream>
int main() {
using It = boost::spirit::istream_iterator;
std::deque<std::string> testcases {
// one multiline case:
"Between, 20, 10, 30\n"
"Between, NaN, NaN, NaN\n"
"Between, \"q\", \"a\", \"z\""
};
// many single line cases for easy test reporting
for (std::string op : {"Greater","Equals"})
for (auto rhs : { "42", "0.0", "true", "\"hello\"" })
for (auto lhs : { "41", "-0.0", "false", "\"bye\"" }) {
testcases.push_front(op + ", " + lhs + ", " + rhs);
}
for (auto testcase : testcases) {
std::cout << "--- Testcase '" << testcase << "' -> ";
std::istringstream iss(testcase);
It f(iss >> std::noskipws), l;
Ast::Invocations parsed;
if (qi::parse(f, l, Grammar<It>(), parsed)) {
for (auto& invocation : parsed) {
try {
std::cout << std::boolalpha << evaluate(invocation) << "; ";
} catch(std::exception const& e) {
std::cout << e.what() << "; ";
}
}
std::cout << "\n";
} else {
std::cout << "Parse failed\n";
}
if (f != l)
std::cout << "Remaining unparsed input: '" << std::string(f,l) << "'\n";
}
}
Prints:
--- Testcase 'Equals, "bye", "hello"' -> false;
--- Testcase 'Equals, false, "hello"' -> Type Mismatch;
--- Testcase 'Equals, -0.0, "hello"' -> Type Mismatch;
--- Testcase 'Equals, 41, "hello"' -> Type Mismatch;
--- Testcase 'Equals, "bye", true' -> Type Mismatch;
--- Testcase 'Equals, false, true' -> false;
--- Testcase 'Equals, -0.0, true' -> false;
--- Testcase 'Equals, 41, true' -> false;
--- Testcase 'Equals, "bye", 0.0' -> Type Mismatch;
--- Testcase 'Equals, false, 0.0' -> true;
--- Testcase 'Equals, -0.0, 0.0' -> true;
--- Testcase 'Equals, 41, 0.0' -> false;
--- Testcase 'Equals, "bye", 42' -> Type Mismatch;
--- Testcase 'Equals, false, 42' -> false;
--- Testcase 'Equals, -0.0, 42' -> false;
--- Testcase 'Equals, 41, 42' -> false;
--- Testcase 'Greater, "bye", "hello"' -> false;
--- Testcase 'Greater, false, "hello"' -> Type Mismatch;
--- Testcase 'Greater, -0.0, "hello"' -> Type Mismatch;
--- Testcase 'Greater, 41, "hello"' -> Type Mismatch;
--- Testcase 'Greater, "bye", true' -> Type Mismatch;
--- Testcase 'Greater, false, true' -> false;
--- Testcase 'Greater, -0.0, true' -> false;
--- Testcase 'Greater, 41, true' -> true;
--- Testcase 'Greater, "bye", 0.0' -> Type Mismatch;
--- Testcase 'Greater, false, 0.0' -> false;
--- Testcase 'Greater, -0.0, 0.0' -> false;
--- Testcase 'Greater, 41, 0.0' -> true;
--- Testcase 'Greater, "bye", 42' -> Type Mismatch;
--- Testcase 'Greater, false, 42' -> false;
--- Testcase 'Greater, -0.0, 42' -> false;
--- Testcase 'Greater, 41, 42' -> false;
--- Testcase 'Between, 20, 10, 30
Between, NaN, NaN, NaN
Between, "q", "a", "z"' -> true; false; true;
boost variant is the easiest way IMHO:
#include <boost/variant.hpp>
#include <boost/operators.hpp>
#include <string>
#include <iostream>
#include <iomanip>
// define the concept of equality in my scripting language
struct is_equal : boost::static_visitor<bool>
{
// x == x is easy
template<class T>
bool operator()(const T& l, const T& r) const {
return l == r;
}
// define the concept of comparing strings to integers
bool operator()(const std::string& l, const int& r) const {
return l == std::to_string(r);
}
// and integers to strings
bool operator()(const int& l, const std::string& r) const {
return (*this)(r, l);
}
};
struct is_less : boost::static_visitor<bool>
{
// x == x is easy
template<class T>
bool operator()(const T& l, const T& r) const {
return l < r;
}
// define the concept of comparing strings to integers
bool operator()(const std::string& l, const int& r) const {
return std::stoi(l) < r;
}
// and integers to strings
bool operator()(const int& l, const std::string& r) const {
return l < std::stoi(r);
}
};
struct emit : boost::static_visitor<std::ostream&>
{
emit(std::ostream& os) : os_(os) {}
// x == x is easy
template<class T>
std::ostream& operator()(const T& l) const {
return os_ << l;
}
std::ostream& operator()(const std::string& s) const {
return os_ << std::quoted(s);
}
std::ostream& os_;
};
struct scriptable_value
: boost::less_than_comparable<scriptable_value>
, boost::equality_comparable<scriptable_value>
{
using variant_type = boost::variant<std::string, int>;
scriptable_value(std::string v) : variant_(std::move(v)) {}
scriptable_value(int v) : variant_(v) {}
variant_type const& as_variant() const {
return variant_;
}
private:
variant_type variant_;
};
bool operator==(scriptable_value const& l, scriptable_value const& r)
{
return boost::apply_visitor(is_equal(), l.as_variant(), r.as_variant());
}
bool operator<(scriptable_value const& l, scriptable_value const& r)
{
return boost::apply_visitor(is_less(), l.as_variant(), r.as_variant());
}
std::ostream& operator<<(std::ostream& os, scriptable_value const& r)
{
return boost::apply_visitor(emit(os), r.as_variant());
}
int main()
{
auto x = scriptable_value(10);
auto y = scriptable_value("10");
auto x2 = scriptable_value(9);
auto y2 = scriptable_value("9");
std::cout << x << " == " << y << " : " << std::boolalpha << (x == y) << std::endl;
std::cout << x << " != " << y << " : " << std::boolalpha << (x != y) << std::endl;
std::cout << x << " == " << y2 << " : " << std::boolalpha << (x == y2) << std::endl;
std::cout << x << " != " << y2 << " : " << std::boolalpha << (x != y2) << std::endl;
std::cout << x << " < " << y << " : " << std::boolalpha << (x < y) << std::endl;
std::cout << x << " >= " << y << " : " << std::boolalpha << (x >= y) << std::endl;
std::cout << x << " < " << y2 << " : " << std::boolalpha << (x < y2) << std::endl;
std::cout << x << " >= " << y2 << " : " << std::boolalpha << (x >= y2) << std::endl;
std::cout << x << " == " << x2 << " : " << std::boolalpha << (x == x2) << std::endl;
std::cout << x << " != " << x2 << " : " << std::boolalpha << (x != x2) << std::endl;
}
expected output:
10 == "10" : true
10 != "10" : false
10 == "9" : false
10 != "9" : true
10 < "10" : false
10 >= "10" : true
10 < "9" : false
10 >= "9" : true
10 == 9 : false
10 != 9 : true
Your fundamental issue is that C++ is a statically-typed language. General-purpose programming languages tend to fall into two rough categories: statically and dynamically typed. In a dynamically-typed language, like Perl, the type of an object is determined at runtime. In a statically-typed language, like C++, the type of an object is specified at compile-time.
That doesn't mean that this is not possible in C++ in a type-safe manner. It is, but it requires some work.
The usual approach is to encapsulate all types into classes that are derived from some base class that defines virtual methods, with the subclasses implementing them. Let's use just ints and floats.
// Forward declarations
class FloatNumber;
class IntNumber;
class Number {
// virtual methods to be defined later.
};
class FloatNumber : public Number {
float value;
// Implements the virtual methods for float values.
};
class IntNumber : public Number {
int value;
// Implements the virtual methods for int values.
};
Now, you can implement basic operations. In the Number base class, you define the conversion methods:
virtual FloatNumber asFloat() const = 0;
virtual IntNumber asInt() const = 0;
In each subclass you will implement these in the obvious manner, returning either *this, if it's the same type, or constructing the other subclass and returning the newly-constructed class.
Now, you can implement basic operations. Say, equals:
virtual bool equals(const Number &other) const =0;
Now, you get to implement this virtual method in each subclass. In FloatNumber::equals(), for example, you would call other.asFloat(), and compare its val to its own val. Ditto for IntNumber::equals(). If both Numbers that are compared are of the same type, this ends up comparing the two values directly; otherwise an automatic type conversion takes place.
Now, this is not a perfect approach, since if the first number is an IntNumber, the FloatNumber ends up getting down-converted to an int, and you really want the conversion to go the other way. There are classical ways to solve this also, in a type-safe manner. But first, you should get this basic approach implemented first, and then worry about handling the various corner-cases.
In this manner you can proceed and build up a class hierarchy that implements generic operation on numbers. This is likely more work than you expected, but this is how to properly do this kind of thing in C++, in a completely type-safe manner. Modern C++ compilers are quite efficient, and the end result will be quite small, and compact.
The final step for you is to read the file, parse the values, and have a simple lookup table that maps "Equals" to the "equals" method, and so on...

Syntax error while parsing file using flex and bison

I am parsing the following file:
BEGIN BLOCK BLK_ROWDEC
NAME cell_rowdec
SIZE UNI_rowdecSize
ITERATE itr_rows
DIRECTION lgDir_rowdec
STRAP STRD1,STRD3,STRD2
WRAP WRD1
VIA VIAB,VIAC,VIAD
ENDS BLK_ROWDEC
My flex and bison file are as follows:
lexa.l
%{
#include <iostream>
#include <stdio.h>
const char s[2] = " ";
#include "yacc.tab.h"
char *token;
#define YY_DECL extern "C" int yylex()
int line_num = 1;
using namespace std;
%}
DOT "."
COLON ":"
SEMICOLON ";"
COMMA ","
ANGLE_LEFT "<"
ANGLE_RIGHT ">"
AT "#"
EQUAL "="
SQUARE_OPEN "["
SQUARE_CLOSE [^\\]"]"
OPENBRACE "\("
CLOSEBRACE "\)"
QUOTE "\""
QUOTE_OPEN "\""
QUOTE_CLOSE [^\\]"\""
SPACE " "
TAB "\t"
CRLF "\r\n"
QUOTED_PAIR "\\"[^\r\n]
DIGIT [0-9]
ALPHA [a-zA-Z]
QTEXT [0-9a-zA-Z!#$%&'()*+,\-.\/:;<=>?#\[\]^_`{|}~]
%%
[ \t] ;
^BEGIN(.*)\r?\n+\s*BEGIN(.*) { printf("\nError : two continous BEGIN is not allowed : "); }
^ENDS(.*)\r?\n+\s*ENDS(.*) { printf("\nError : two continous END is not allowed : \n"); }
NAME { yylval.sval = strdup(yytext);
return TOK_NAME; }
SIZE { yylval.sval = strdup(yytext);
return TOK_SIZE; }
ITERATE { yylval.sval = strdup(yytext);
return TOK_ITERATE; }
DIRECTION { yylval.sval = strdup(yytext);
return TOK_DIRECTION; }
STRAP { yylval.sval = strdup(yytext);
return TOK_STRAP; }
WRAP { yylval.sval = strdup(yytext);
return TOK_WRAP; }
VIA { yylval.sval = strdup(yytext);
return TOK_VIA; }
ENDS { yylval.sval = strdup(yytext);
return TOK_END; }
BEGIN { yylval.sval = strdup(yytext);
return TOK_BEGIN; }
BLOCK { yylval.sval = strdup(yytext);
return TOK_BLOCK; }
[a-zA-Z0-9_,]+ { yylval.sval = strdup(yytext);
return TOK_STRING; }
{SPACE}* { return TOK_SPACE; }
^ENDS(.*)$ {}
^{CRLF} { return TOK_EMPTY_LINE; }
{CRLF} {}
. {}/* ignore unknown chars */
\n { ++line_num; return ENDL; }
yacca.y
%{
#include <cstdio>
#include <cstring>
#include <iostream>
#include <stdio.h>
#define YYDEBUG 1
using namespace std;
extern "C" int yylex();
extern "C" FILE *yyin;
extern int line_num;
void yyerror(const char* s);
%}
// Symbols.
%union
{
char* sval;
};
%token <sval> TOK_NAME
%token <sval> TOK_SIZE
%token <sval> TOK_STRING
%token <sval> TOK_ITERATE
%token <sval> TOK_DIRECTION
%token <sval> TOK_STRAP
%token <sval> TOK_WRAP
%token <sval> TOK_VIA
%token <sval> TOK_EMPTY_LINE
%token <sval> TOK_BLOCK
%token <sval> TOK_LINE
%token <sval> TOK_BEGIN
%token <sval> TOK_END
%token TOK_SPACE
%token END ENDL
%%
language : program ;
program : block
| program block
;
block : TOK_BEGIN TOK_BLOCK TOK_SPACE TOK_STRING blockcontents TOK_END TOK_SPACE TOK_STRING
{
cout << endl << "SHAILAVI" << $4 << " ";
}
;
blockcontents : item
| blockcontents item
;
item : TOK_SPACE TOK_NAME TOK_SPACE TOK_STRING
{
cout << endl << "Value:" << $2 << "->" << $4 << " ";
}
| TOK_SPACE TOK_SIZE TOK_SPACE TOK_STRING { cout << $2 << "->" << $4 << " "; }
| TOK_SPACE TOK_ITERATE TOK_SPACE TOK_STRING { cout << $2 << "->" << $4 << " "; }
| TOK_SPACE TOK_DIRECTION TOK_SPACE TOK_STRING { cout << $2 << "->" << $4 << " " << endl; }
| TOK_SPACE TOK_STRAP TOK_SPACE TOK_STRING { cout << "ref:" << $2 << "->" << $4 << " "; }
| TOK_SPACE TOK_WRAP TOK_SPACE TOK_STRING { cout << $2 << "->" << $4 << " "; }
| TOK_SPACE TOK_VIA TOK_SPACE TOK_STRING { cout << $2 << "->" << $4 << " " << endl; }
;
%%
int main(void) {
FILE * pt = fopen("file", "r" );
if(!pt)
{
cout << "Bad Input.Noexistant file" << endl;
return -1;
}
yyin = pt;
do
{
yydebug = 1;
yyparse();
}while (!feof(yyin));
}
void yyerror(const char *s) {
cout << "parse error on line " << line_num << "! Message: " << s << endl;
exit(-1);
}
#include "lex.yy.c"
Compilation steps:
flex lexa.l
bison -d yacca.y
g++ yacca.tab.c -lfl -o scanner.exe
At the time of execution it gives syntax error near blockcontents
Please help me to identify the mistake I have done.
Thank You so much.
It took me a while, but I've found the flaw.
In your lexer, you skip all sequences of tabs and blanks (first rule).
But your parser expects white space every now and then. Hence the syntax error.
Since you don't do anything with the white space, simply eat them within the lexer (as you already do now actually, but it is better to eliminate the {SPACE}* rule too) and eliminate the TOK_SPACE in the parser.
---- edit to give some hints ----
What I did to track down the bug is:
make the lexer verbose
I added (hash signs omitted; it confuses the renderer for some reason)
#ifdef DEBUG
#define RETURN(x) cerr << "\n--> found " << #x << "\n"; return x;
#else
#define RETURN(x) return x;
#endif
and replaced all "return something" by RETURN(something)
I compile the bison/flex files separately and link them afterwards
flex lexa.l && \
bison -d yacca.y && \
g++ -c -DDEBUG -I . lex.yy.c && \
g++ -c -I . yacca.tab.c && \
g++ lex.yy.o yacca.tab.o -o scanner
(working on linux here)
As requested the working example
%{
#include <cstdio>
#include <cstring>
#include <iostream>
#include <stdio.h>
#define YYDEBUG 1
using namespace std;
extern "C" int yylex();
extern "C" FILE *yyin;
extern int line_num;
void yyerror(const char* s);
%}
// Symbols.
%union
{
char* sval;
};
%token TOK_NAME
%token TOK_SIZE
%token TOK_STRING
%token TOK_ITERATE
%token TOK_DIRECTION
%token TOK_STRAP
%token TOK_WRAP
%token TOK_VIA
%token TOK_EMPTY_LINE
%token TOK_BLOCK
%token TOK_LINE
%token TOK_BEGIN
%token TOK_END
%token END ENDL
%%
language : program ;
program : block
| program block
;
block : TOK_BEGIN TOK_BLOCK TOK_STRING blockcontents TOK_END TOK_STRING
{
cout << endl << "SHAILAVI" << $3 << " ";
}
;
blockcontents : item
| blockcontents item
;
item : TOK_NAME TOK_STRING { cout << endl << "Value:" << $1 << "->" << $2 << " "; }
| TOK_SIZE TOK_STRING { cout << $1 << "->" << $2 " << $2 " << $2 " << $2 << " "; }
| TOK_WRAP TOK_STRING { cout << $1 << "->" << $2 << " "; }
| TOK_VIA TOK_STRING { cout << $1 << "->" << $2 << " " << endl; }
;
%%
int main(void) {
FILE * pt = fopen("./input", "r" );
if(!pt)
{
cout << "Bad Input.Nonexistent file" << endl;
return -1;
}
yyin = pt;
do
{
yydebug = 1;
yyparse();
}while (!feof(yyin));
}
void yyerror(const char *s) {
cout << "parse error on line " << line_num << "! Message: " << s << endl;
exit(-1);
}
extern "C" int yywrap()
{
return (1 == 1);
}
And the lexer
%{
#include
#include
const char s[2] = " ";
#include "yacca.tab.h"
char *token;
#define YY_DECL extern "C" int yylex()
int line_num = 1;
#ifdef DEBUG
#define RETURN(x) cerr << "\n--> found " << #x << "\n"; return x;
#else
#define RETURN(x) return x;
#endif
using namespace std;
%}
DOT "."
COLON ":"
SEMICOLON ";"
COMMA ","
ANGLE_LEFT ""
AT "#"
EQUAL "="
SQUARE_OPEN "["
SQUARE_CLOSE [^\\]"]"
OPENBRACE "\("
CLOSEBRACE "\)"
QUOTE "\""
QUOTE_OPEN "\""
QUOTE_CLOSE [^\\]"\""
SPACE " "
TAB "\t"
CRLF "\r\n"
QUOTED_PAIR "\\"[^\r\n]
DIGIT [0-9]
ALPHA [a-zA-Z]
QTEXT [0-9a-zA-Z!#$%&'()*+,\-.\/:;?#\[\]^_`{|}~]
/* [ \t] ; */
%%
^BEGIN(.*)\r?\n+\s*BEGIN(.*) { printf("\nError : two continous BEGIN is not allowed : "); }
^ENDS(.*)\r?\n+\s*ENDS(.*) { printf("\nError : two continous END is not allowed : \n"); }
NAME { yylval.sval = strdup(yytext);
RETURN(TOK_NAME); }
SIZE { yylval.sval = strdup(yytext);
RETURN(TOK_SIZE); }
ITERATE { yylval.sval = strdup(yytext);
RETURN(TOK_ITERATE); }
DIRECTION { yylval.sval = strdup(yytext);
RETURN(TOK_DIRECTION); }
STRAP { yylval.sval = strdup(yytext);
RETURN(TOK_STRAP); }
WRAP { yylval.sval = strdup(yytext);
RETURN(TOK_WRAP); }
VIA { yylval.sval = strdup(yytext);
RETURN(TOK_VIA); }
ENDS { yylval.sval = strdup(yytext);
RETURN(TOK_END); }
BEGIN { yylval.sval = strdup(yytext);
RETURN(TOK_BEGIN); }
BLOCK { yylval.sval = strdup(yytext);
RETURN(TOK_BLOCK); }
[a-zA-Z0-9_,]+ { yylval.sval = strdup(yytext); RETURN(TOK_STRING); }
^ENDS(.*)$ {}
^{CRLF} { RETURN(TOK_EMPTY_LINE); }
{CRLF} {}
. {}/* ignore unknown chars */
\n { ++line_num; /* RETURN(ENDL); */ }
There's only one problem left. It doesn't really like the EOF. I'll leave that as an exercise.

Resources