Custom lua bytecode / opcode (compiler / reader / parser / decompiler) - c++

01/02/05/70/72/69/6E/74/01/68/01/02/00/00/01/06/A3/00/00/00/A4/00/01/00/00/00/00/40/6F/01/02/00/9F/00/02/01/82/00/01/00/03/03/01/04/00/00/00/40/03/02/00/00/06/01/00/00/00/00/00/00/00 is bytecode for
print"h"
I need a way to make a compiler that can turn any bytecode into a semi accurate script.
I've already made one but its very bad and it only supports the Clouse, getglobal, setglobal, loadk, loadnil, loadnumber, loadbool, call, gettablek, clearstack, init, vararg, return and length. Most of the time it will come out wrong..
Code:
#include <iostream>
#include <Windows.h>
#include <string>
#include <sstream>
#include <algorithm>
#include <iterator>
#include <vector>
namespace opcodes {
std::string GETGLOBAL = "A4";
std::string SETGLOBAL = "18";
std::string GETTABLEK = "4D";
std::string SETTABLEK = "30";
std::string LOADK = "6F";
std::string RETURN = "82";
std::string LOADNUMBER = "8C";
std::string CALL = "9F";
std::string CLEARSTACK = "A3";
std::string LOADBOOL = "A9";
std::string LOADNIL = "C6";
std::string CLOSURE = "D9";
std::string INIT = "C0";
std::string LEN = "1C";
std::string SUB = "26";
std::string FORLOOP = "8B";
std::string FORPREP = "A8";
std::string VARARG = "DD";
std::string SETUPVAL = "DE";
};
std::vector<std::string> FuncsGlob;
std::vector<std::string> locals;
std::stringstream ss;
std::string inp = "";
std::vector<std::string> hexs;
std::string script;
std::string Value;
std::string lf = "";
std::string HTA(std::string hex)
{
// initialize the ASCII code string as empty.
std::string ascii = "";
for (size_t i = 0; i < hex.length(); i += 2)
{
// extract two characters from hex string
std::string part = hex.substr(i, 2);
// change it into base 16 and
// typecast as the character
char ch = stoul(part, nullptr, 16);
// add this char to final ASCII string
ascii += ch;
}
return ascii;
}
void Split(const std::string& s, char c,
std::vector<std::string>& v) {
std::string::size_type i = 0;
std::string::size_type j = s.find(c);
while (j != std::string::npos) {
v.push_back(s.substr(i, j - i));
i = ++j;
j = s.find(c, j);
if (j == std::string::npos)
v.push_back(s.substr(i, s.length()));
}
}
static bool isF(std::string func) {
std::vector<std::string> funcs = {"wait", "warn", "print"};
for (int i = 0; i < funcs.size(); i++)
{
if (funcs[i] == func)
{
return true;
}
}
return false;
}
int main()
{
std::cout << "Enter bytecode: ";
inp = "01/02/05/70/72/69/6E/74/01/68/01/02/00/00/01/06/A3/00/00/00/A4/00/01/00/00/00/00/40/6F/01/02/00/9F/00/02/01/82/00/01/00/03/03/01/04/00/00/00/40/03/02/00/00/06/01/00/00/00/00/00/00/00";
std::cout << std::endl;
Split(inp, '/', hexs);
if (hexs[0] == "01") {
int stringtables = std::stoi(hexs[1]);
int FSize = 2;
std::string ValueName;
int psr = 1;
int clsr = 0;
int init = 0;
int conv = 0;
int lk_r = 0;
int slitthroat = 0;
int b_s = std::stoi(hexs[FSize]);
for (unsigned int sti = 0; sti < stringtables; sti++) {
for (int cur = FSize + psr; cur < FSize + b_s + 1; cur++) {
ValueName = (std::string)HTA((std::string)hexs[cur]);
Value += ValueName;
conv = cur;
}
psr++;
b_s = std::stoi(hexs[conv + 1]) + 1;
FSize = conv++;
FuncsGlob.insert(FuncsGlob.end(), Value);
Value = "";
ValueName = "";
}
for (int nig = conv; nig < hexs.size(); ++nig) {
locals.resize(hexs.size() + 1);
FuncsGlob.resize(hexs.size() + 1);
if (hexs[nig] == opcodes::LOADK) {
script += "\"" + FuncsGlob[lk_r] + "\"";
slitthroat++;
lk_r++;
}
if (hexs[nig] == opcodes::GETGLOBAL) {
if (isF(FuncsGlob[lk_r]))
script += FuncsGlob[lk_r] + "(";
else
script += FuncsGlob[lk_r] + ".";
lk_r++;
}
if (hexs[nig] == opcodes::CALL) {
if (clsr == 0) {
if (script[script.length() - 1] == '.')
script = script.erase(script.length() - 1);
script += ")";
}
}
if (hexs[nig] == opcodes::CLEARSTACK) {
lk_r = 0;
}
if (hexs[nig] == opcodes::GETTABLEK) {
script += FuncsGlob[lk_r] + ".";
lk_r++;
}
if (hexs[nig] == opcodes::LOADNUMBER) {
script += std::stoi(hexs[nig + 2]);
}
if (hexs[nig] == opcodes::LOADBOOL) {
if (hexs[nig + 2] == "00") {
script += "false";
}
else if (hexs[nig + 2] == "01") {
script += "true";
}
}
if (hexs[nig] == opcodes::LOADNIL) {
script += "nil";
}
if (hexs[nig] == opcodes::INIT) {
init++;
}
if (hexs[nig] == opcodes::CLOSURE) {
if (init > 0) {
script = "(function() " + script + "end)()";
init--;
clsr++;
}
else {
script = "(function() " + script + "end)";
clsr++;
}
}
if (hexs[nig] == opcodes::RETURN) {
clsr = 0;
}
if (hexs[nig] == opcodes::VARARG) {
script += "...";
}
if (hexs[nig] == opcodes::LEN) {
script += "#";
}
}
std::cout << std::endl << script;
}
}

Related

getting "_throw_bad_array_new_lengthv" when trying to use std::vector::push_back inside a class

I'm creating a custom language parser in C++, I'm struggling with a runtime error, where I have a std::vector<std::string> member of a class with a constructor.
The full error is:
The procedure entry point
_ZSt28__throw_bad_array_new_lengthv could no be located
in the dynamic link library
"path_to_executable"
This error is throw whenever I try to use std::vector::push_back in my code, but shouldn't std::vector be a dynamically sized data container? Why is this error occurring?
Some of my codes:
//"lib/cursor.h"
#ifndef T_CURSOR
#define T_CURSOR
#include <iostream>
struct Cursor
{
private:
std::string input;
int inputLength;
char getChar(int p);
public:
char character;
int pos;
int line;
int column;
bool eof;
bool lineBreak;
Cursor(std::string i);
void walk(bool back = false);
void walkTimes(int times, bool back = false);
void move(int toPos);
void skipIgnore();
std::string toString();
};
#endif
//"lib/cursor.cpp"
#include <sstream>
#include "cursor.h"
Cursor::Cursor(std::string i)
{
this->input = i;
this->inputLength = i.length();
this->character = i.at(0);
this->pos = 0;
this->line = 0;
this->column = 0;
this->eof = false;
this->lineBreak = false;
}
char Cursor::getChar(int pos)
{
if (pos < 0 || pos >= this->inputLength)
{
return EOF;
}
return this->input.at(pos);
}
void Cursor::walk(bool back)
{
if (back)
{
this->pos--;
this->column--;
if (this->lineBreak)
{
this->line--;
this->column = 0;
for (int i = this->pos - 1; i >= 0; i--)
{
if (this->getChar(i) == '\n')
break;
this->column++;
}
}
}
else
{
this->pos++;
this->column++;
if (this->lineBreak)
{
this->line++;
this->column = 0;
}
}
this->character = this->getChar(this->pos);
this->eof = this->character == EOF;
this->lineBreak = this->character == '\n';
}
void Cursor::walkTimes(int times, bool back)
{
for (int i = 0; i < times; i++)
{
this->walk(back);
}
}
void Cursor::move(int pos)
{
if (pos < 0)
pos = 0;
if (pos > this->inputLength - 1)
pos = this->inputLength - 1;
this->pos = 0;
this->character = this->input.at(0);
this->line = 0;
this->column = 0;
this->eof = false;
this->lineBreak = this->character == '\n';
while (this->pos < pos)
this->walk();
}
void Cursor::skipIgnore()
{
while (this->character == ' ' ||
this->character == '\n' ||
this->character == '\t')
this->walk();
if (this->character == '#')
{
while (!this->eof && this->character != '\n')
this->walk();
}
while (this->character == ' ' ||
this->character == '\n' ||
this->character == '\t')
this->walk();
}
std::string Cursor::toString()
{
std::stringstream ss("");
ss << "(P:" << this->pos;
ss << " L:" << this->line;
ss << " C:" << this->column;
ss << " \"" << this->character << "\")";
return ss.str();
}
//"lib/lexer.h"
#ifndef T_LEXER
#define T_LEXER
#include <iostream>
#include <vector>
#include "cursor.h"
class Lexer
{
private:
std::string input;
protected:
Cursor cursor;
std::vector<std::string> matchStack;
std::vector<std::vector<Cursor>> cursorStack;
public:
Lexer(std::string input);
std::string getStr(int pos);
void setStr(int pos, std::string str);
Cursor getCursorStart(int pos);
Cursor getCursorEnd(int pos);
bool match(std::string str);
};
#endif
//"lib/lexer.cpp"
#include "lexer.h"
Lexer::Lexer(std::string input) : cursor(input)
{
this->input = input;
}
std::string Lexer::getStr(int pos)
{
if (this->matchStack.size() == 0)
return this->input;
while (pos < 0)
pos += this->matchStack.size();
while (pos >= this->matchStack.size())
pos -= this->matchStack.size();
return this->matchStack[pos];
}
void Lexer::setStr(int pos, std::string str)
{
if (this->matchStack.size() == 0)
return;
while (pos < 0)
pos += this->matchStack.size();
while (pos >= this->matchStack.size())
pos -= this->matchStack.size();
this->matchStack[pos] = str;
}
Cursor Lexer::getCursorStart(int pos)
{
if (this->cursorStack.size() == 0)
return Cursor(this->input);
while (pos < 0)
pos += this->cursorStack.size();
while (pos >= this->cursorStack.size())
pos -= this->cursorStack.size();
return this->cursorStack[pos][0];
}
Cursor Lexer::getCursorEnd(int pos)
{
if (this->cursorStack.size() == 0)
return Cursor(this->input);
while (pos < 0)
pos += this->cursorStack.size();
while (pos >= this->cursorStack.size())
pos -= this->cursorStack.size();
return this->cursorStack[pos][1];
}
bool Lexer::match(std::string str)
{
this->cursor.skipIgnore();
const std::string ss = this->input.substr(this->cursor.pos, str.length());
if (ss == str)
{
this->matchStack.push_back(ss); // Getting error if I include this line
const Cursor startCursor = this->cursor;
this->cursor.walkTimes(str.length());
const Cursor endCursor = this->cursor;
this->cursorStack.push_back({startCursor, endCursor});
return true;
}
return false;
}
//"test.cpp"
#include "lib/cursor.h"
#include "lib/lexer.h"
#include <iostream>
using namespace std;
int main()
{
string input = "Something to test";
Lexer lexer = Lexer(input);
cout << "Start" << endl;
cout << lexer.match("Something") << endl;
return 0;
}
I'm compiling my program with g++ on Windows: g++ test.cpp lib/cursor.cpp lib/lexer.cpp -o test.exe
I got the same error when compiling in command line using g++. But it works well in code blocks. To solve this problem try compiling with -static-libstdc++. It should solve the problem.

pattern matching (codejam round 1A previous year) solution not working

I am trying previous year's codejam question of round 1A
link to question
i have submitted this code(start reading from main method, for ease)-
#include <bits/stdc++.h>
using namespace std;
#define range(t) for (int i = 0; i < t; i++)
#define rangeG(i, t) for (i = 0; i < t; i++)
#define printVec(vec) \
for (auto c : vec) \
{ \
cout << c << endl; \
}
vector<string> separate(string s)
{
vector<string> result;
range(s.size())
{
if (s[i] == '*')
{
string temp = s.substr(0, i + 1);
if (temp.size() > 1)
{
result.push_back(temp);
}
s = s.substr(i, s.size());
i = 0;
}
else if (i == (s.size() - 1))
{
string temp = s.substr(0, i + 1);
result.push_back(temp);
s = s.substr(i, s.size());
}
}
return result;
}
void removeAsterisk(string &s)
{
s.erase(remove(s.begin(), s.end(), '*'), s.end());
}
bool setStart(string s, string &start)
{
bool possible = 1;
removeAsterisk(s);
range(min(s.size(), start.size()))
{
if (s[i] != start[i])
{
possible = 0;
}
}
if (possible)
{
if (s.size() >= start.size())
{
start = s;
}
}
return possible;
}
bool setEnd(string s, string &end)
{
bool possible = 1;
removeAsterisk(s);
range(min(s.size(), end.size()))
{
if (s[s.size() - 1 - i] != end[end.size() - 1 - i])
{
possible = 0;
}
}
if (possible)
{
if (s.size() >= end.size())
{
end = s;
}
}
return possible;
}
void solve()
{
int n;
cin >> n;
vector<string> allS;
bool possible = 1;
string start = "";
string end = "";
string middle = "";
string result = "";
while (n--)
{
string str;
cin >> str;
if (count(str.begin(), str.end(), '*') == 0)
{
result = str;
}
vector<string> temp = separate(str);
for (string s : temp)
{
if (s[0] != '*')
{
possible = setStart(s, start);
}
if (s[s.size() - 1] != '*')
{
possible = setEnd(s, end);
}
if (possible && count(s.begin(), s.end(), '*') == 0)
{
result = s;
break;
}
if (s[0] == '*' && s[s.size() - 1] == '*')
{
removeAsterisk(s);
middle += s;
}
}
}
if (possible)
{
if (result.size() == 0)
{
result = start + middle + end;
}
cout << result << "\n";
}
else
{
cout << "*\n";
}
}
int main()
{
ios_base::sync_with_stdio(false);
cin.tie(NULL);
int t = 0;
cin >> t;
range(t)
{
cout << "Case #" << i + 1 << ": ";
solve();
}
return 0;
}
it seems correct to me and i have tested many times for many examples, but it is losing in test set-1(exactly one * (asterisk) character and and always the first character of string). Can anyone tell what's wrong?
you can consider code of first ranked here (it has all solutions,check only for "pattern matching" task) for help. I know that the wrong answer is an edge case and if it passes test set 1 then it will pass others.

Splitting a string based on multiple string separators in c++

I'm writing my code in C++98 using only standard libraries.
I'm trying to write some code to split a string in multiple substrings each delimited by the string "OK" or the string "ERROR".
Each substring should be put in the mysubstring array.
This is the code I wrote for a single separator:
void split_string()
{
for (unsigned short k=0;k<10;k++)
{
mysubstring[k]=""; //resetting all substrings
}
string separator = "OK";
size_t pos = 0;
unsigned short index=0;
while ((pos = str_to_split.find(separator)) != std::string::npos) {
mysubstring[index] = str_to_split.substr(0, pos);
str_to_split.erase(0, pos + separator.length());
index++;
}
This single separator version works fine.
Then I tried to upgrade to the two separators:
void split_string()
{
for (unsigned short k=0;k<10;k++)
{
mysubstring[k]=""; //resetting all substrings
}
string okseparator = "OK";
string koseparator = "ERROR";
size_t okpos = 0;
size_t kopos = 0;
unsigned short index=0;
while ((okpos = string_to_split.find(okseparator)) != std::string::npos)
{
while ((kopos = string_to_split.find(koseparator)) != std::string::npos)
{
if (okpos <= kopos)
{
mysubtring[index] = string_to_split.substr(0, okpos + okseparator.length());
string_to_split.erase(0, okpos + okseparator.length());
index++;
}
else
{
mysubstring[index] = string_to_split.substr(0, kopos + koseparator.length());
string_to_split.erase(0, kopos + koseparator.length());
index++;
}
}
}
while ((kopos = string_to_split.find(koseparator)) != std::string::npos)
{
mysubtring[index] = string_to_split.substr(0, kopos + koseparator.length());
string_to_split.erase(0, kopos + koseparator.length());
index++;
}
}
The idea here is that I stay inside the first while loop untill all "OK" are consumed, then it enters the last while to finish off all "ERROR" left.
The substrings should enter the mysubstring array in the same order they are in the string_to_split original string.
Sadly I can't get it to work, could you help me ?
Example to test and verify:
#include <iostream>
#include <string.h>
void split_string();
string str_to_split = "skdjfnsdjknfjk OK fkjsnfjksdnfjnsdjkfn ERROR skjdfnjksdnf OK sjkdnfjksdnfjERROR jnfjnsdjfnsjdknfjkn OK";
use namespace std;
int main()
{
split_string();
return 0;
}
Figured it out:
void split_string()
{
for (unsigned short k=0;k<10;k++)
{
mysubstring[k]=""; //resetting all substrings
}
string okseparator = "OK";
string koseparator = "ERROR";
size_t okpos = 0;
size_t kopos = 0;
unsigned short index=0;
while (1)
{
okpos = string_to_split.find(okseparator);
kopos = string_to_split.find(koseparator);
if (okpos < kopos)
{
mysubstring[index] = string_to_split.substr(0, okpos + okseparator.length());
string_to_split.erase(0, okpos + okseparator.length());
index++;
}
else if (okpos > kopos)
{
mysubstring[index] = string_to_split.substr(0, kopos);
string_to_split.erase(0, kopos + koseparator.length());
index++;
}
else
{
break;
}
}
}
I get the position for both separators but I consider only the closest one.
The while(1) terminates when both the separators have the same position (string::npos = max(size_t)).

getline() until end of input C++

Here is my code. It is supposed to get input until end of input and place that input into the string data. Then it is supposed to tokenize the input using the delimiter "#". I then repeatedly call my function nexttoken() to store the tokens in variables.
std::istream_iterator<char> it(std::cin);
std::istream_iterator<char> end;
std::string data(it,end);
std::string delimiter = "#";
StringTokenizer strtok(data,delimiter);
std::string t1 = strtok.nextToken();
std::string t2 = strtok.nextToken();
This all works when I pass a file through on command line like this:
program.exe <testcase1.txt
testcase1.txt
S A B #
S -> a A #
S -> z B #
A -> b B c B #
B -> d A #
##
output
S A B
a: 1
b: 1
c: 1
d: 1
z: 1
everything checks out and works.
My problem is this: When I push run in my IDE I can type the input in manually but when I do there is no way to make the program accept the input except if I press ctrl-z. This problem also persists in linux when I pass a file through in terminal, it just hangs there letting me type infinite lines.
here is a smaller version of my code that only counts 3 tokens and only checks for a,b and c
main.cpp
#include <stdio.h>
#include <stdlib.h>
#include <iostream>
#include <string>
#include <iterator>
#include <algorithm>
#include <cstddef>
#include "StringTokenizer.h"
int countSubstring(const std::string& str, const std::string& sub)
{
if (sub.length() == 0) return 0;
int count = 0;
for (size_t offset = str.find(sub); offset != std::string::npos;
offset = str.find(sub, offset + sub.length()))
{
++count;
}
return count;
}
int main(int argc, char* argv[1])
{
int task;
if (argc < 2)
{
std::cout << "Error: missing argument\n";
return 1;
}
task = atoi(argv[1]);
switch(task){
case 0:
{
std::istream_iterator<char> it(std::cin);
std::istream_iterator<char> end;
std::string data(it,end);
std::string delimiter = "#";
StringTokenizer strtok(data,delimiter);
int a = 0;
int b = 0;
int c = 0;
//reading the first token and puting it in tk1
std::string t1 = strtok.nextToken();
std::string tk1(t1);
tk1.erase(0, tk1.find_first_not_of(" "));
tk1.erase(tk1.find_last_not_of(" ")+1);
// token 2 and 3 are different because 1 is always the same format
std::string t2 = strtok.nextToken();
std::string tk2(t2);
if(countSubstring(tk2,"a") > 0)
{
a = a + 1;
}
if(countSubstring(tk2,"b") > 0)
{
b=b + 1;
}
if(countSubstring(tk2,"c") > 0)
{
c=c + 1;
}
std::string t3 = strtok.nextToken();
std::string tk3(t3);
if(countSubstring(tk3,"a") > 0)
{
a = a + 1;
}
if(countSubstring(tk3,"b") > 0)
{
b=b + 1;
}
if(countSubstring(tk3,"c") > 0)
{
c=c + 1;
}
// this is where the output is
std::cout << tk1 << std::endl;
if(a > 0)
{
std::cout << "a: " << a <<std::endl;
}
if(b > 0)
{
std::cout << "b: " << b <<std::endl;
}
if(c > 0)
{
std::cout << "c: " << c <<std::endl;
}
}
break;
//////////////////////////////////////////////////
case 1:
break;
case 2:
break;
default:
std::cout << "Error: unrecognized task number " << task << "\n";
break;
}
return 0;
}
StringTokenizer.h
#ifndef INCLUDE_STRINGTOKENIZER_H
#define INCLUDE_STRINGTOKENIZER_H
#include <stdio.h>
#include <stdlib.h>
#include <iostream>
#include <string>
class StringTokenizer
{
public:
StringTokenizer(const std::string& _str, const std::string& _delim);
~StringTokenizer(){};
std::string nextToken();
std::string nextToken(const std::string& delim);
private:
std::string token_str;
std::string delim;
};
#endif
StringTokenizer.cpp
#include "StringTokenizer.h"
StringTokenizer::StringTokenizer(const std::string& _str, const std::string& _delim)
{
if ((_str.length() == 0) || (_delim.length() == 0)) return;
token_str = _str;
delim = _delim;
/*
Remove sequential delimiter
*/
unsigned int curr_pos = 0;
while(true)
{
if ((curr_pos = token_str.find(delim,curr_pos)) != std::string::npos)
{
curr_pos += delim.length();
while(token_str.find(delim,curr_pos) == curr_pos)
{
token_str.erase(curr_pos,delim.length());
}
}
else
break;
}
/*
Trim leading delimiter
*/
if (token_str.find(delim,0) == 0)
{
token_str.erase(0,delim.length());
}
/*
Trim ending delimiter
*/
curr_pos = 0;
if ((curr_pos = token_str.rfind(delim)) != std::string::npos)
{
if (curr_pos != (token_str.length() - delim.length())) return;
token_str.erase(token_str.length() - delim.length(),delim.length());
}
}
std::string StringTokenizer::nextToken()
{
if (token_str.length() == 0)
return "";
std::string tmp_str = "";
unsigned int pos = token_str.find(delim,0);
if (pos != std::string::npos)
{
tmp_str = token_str.substr(0,pos);
token_str = token_str.substr(pos+delim.length(),token_str.length()-pos);
}
else
{
tmp_str = token_str.substr(0,token_str.length());
token_str = "";
}
return tmp_str;
}
std::string StringTokenizer::nextToken(const std::string& delimiter)
{
if (token_str.length() == 0)
return "";
std::string tmp_str = "";
unsigned int pos = token_str.find(delimiter,0);
if (pos != std::string::npos)
{
tmp_str = token_str.substr(0,pos);
token_str = token_str.substr(pos + delimiter.length(),token_str.length() - pos);
}
else
{
tmp_str = token_str.substr(0,token_str.length());
token_str = "";
}
return tmp_str;
}
1: How do I change my code so that It will stop searching for input when I am done typing? or when it can see ## has been typed? (## marks the end of the input)
2: Is this even possible with my current code?
Both Linux and my IDE compile with g++
You are using input streams from std::cid to read the data, which will only stop when you reach the end-of-file, which is why you need to terminate the input with Ctrl-z in windows and Ctrl-d in Linux.
The simplest change is reading line by line and processing them independently. That will allow you to read the termination marker ## and not proceed further (assuming that the marker is actually two # followed by a new line).
std::string line;
while (std::getline(std::cin, line)) {
if (line == "##") break;
// process a single line
}
If there's no guarantee that the delimiter is followed by a single line, you may need to read character by character, but that is unlikely.

Delete first and last 'X' character from character array

I'm trying to delete first 'w' and last 'w' from a string.
I deleted the first 'w', but couldn't delete the last one, and here is my code:
char str1[80], *pstr1, *pstr2;
cout << "Enter a String:\n";
gets_s(str1);
pstr1 = str1;
pstr2 = new char[strlen(str1)];
int n = strlen(str1) + 1, k = 0, i = 0;
bool s = true;
while (k < n+1)
{
if (strncmp((pstr1 + k), "w", 1) != 0)
{
*(pstr2 + i) = *(pstr1 + k);
i++;
k++;
}
else if(s == true)
{
k++;
s = false;
}
else
{
*(pstr2 + i) = *(pstr1 + k);
i++;
k++;
}
}
Make your life easy and use std::string with find_first_of, find_last_of and erase.
#include <string>
void erase_first_of(std::string& s, char c)
{
auto pos = s.find_first_of(c);
if (pos != std::string::npos)
{
s.erase(pos, 1);
}
}
void erase_last_of(std::string& s, char c)
{
auto pos = s.find_last_of(c);
if (pos != std::string::npos)
{
s.erase(pos, 1);
}
}
#include <iostream>
int main()
{
std::string s = "hellow, worldw\n";
erase_first_of(s, 'w');
erase_last_of(s, 'w');
std::cout << s;
}