So far I have managed to put together a lexer and a stack in the hopes of achieving a LL1 parser. I am doing this purely to understand how parsing works, and maybe to use these ideas in future projects. I understand there are much better frameworks out there like json-cpp and rapid-json but I would like to understand this for myself.
The header file is give below.
#pragma once
#include <string>
#include <vector>
#include <map>
#include <variant>
#include <fstream>
#include <stack>
#include "Helper.h"
// Debugging
#include <iostream>
// Types to store JSON ouput
struct jlist;
struct jobject;
using json_value = std::variant<int, float, bool, std::string, jlist, jobject>;
enum tag { int_value, float_value, string_value, list, object };
struct jlist {
tag type;
std::vector<json_value *> vector_value;
};
struct jobject {
tag type;
std::map<std::string, json_value *> map_value;
};
class JSONParser
{
public:
JSONParser();
~JSONParser();
void parseFile(std::string);
private:
std::stack<std::string> s;
bool checkDeliminator(char);
std::vector<std::string> lexer(std::ifstream &);
void parser(std::vector<std::string> &);
void transitionTable(std::string cursor);
};
The implementation is as follows.
#include "genetic-optimization/JSONParser.h"
JSONParser::JSONParser() {
}
JSONParser::~JSONParser() = default;
void JSONParser::parseFile(std::string FILE) {
std::ifstream configfile(FILE);
std::vector<std::string> scan = lexer(configfile);
parser(scan);
}
bool JSONParser::checkDeliminator(char piece) {
switch (piece) {
case '[':
return true;
case ']':
return true;
case '{':
return true;
case '}':
return true;
case ':':
return true;
case ',':
return true;
case '"':
return true;
default:
return false;
}
}
std::vector<std::string> JSONParser::lexer(std::ifstream & configfile) {
char piece;
std::string capture = "";
std::string conversion;
std::vector<std::string> capture_list;
while(configfile >> piece) {
if (checkDeliminator(piece)) {
conversion = piece;
if (capture != "") {
capture_list.push_back(capture);
capture_list.push_back(conversion);
capture = "";
} else {
capture_list.push_back(conversion);
}
} else {
capture += piece;
}
}
return capture_list;
}
void JSONParser::parser(std::vector<std::string> & scan) {
for (auto it = scan.begin(); it != scan.end(); ++it) {
std::cout << *it << "\n"; // Make sure the lexer works
transitionTable(*it);
}
}
void JSONParser::transitionTable(std::string cursor) {
if(s.empty()) {
s.push(cursor);
} else {
if (s.top() == "[") {
s.push(cursor);
} else if (s.top() == "]") {
s.pop();
} else if (s.top() == "{") {
s.push(cursor);
} else if (s.top() == "}") {
s.pop();
}
}
}
I am unsure of how to proceed from here but have been using the json grammar as a starting point and the following tutorial for guidance.
json -> element
value -> object|array|string|number|bool|
object -> {}|{members}
members -> member|member,members
member -> string:element
array -> []|[elements]
elements -> element|element,elements
element -> value
I have three main problems.
The JSON grammar seems to have left indirect recursion. Since the grammar is not as simple as that shown in the tutorial I do not know how to eliminate it.
I do not know how to generate the parse table (finite state machine), specifically for something like First(object), what would this be? Is there any resource that has produced a parse table for JSON and might point me in the right direction?
The tutorial seems more to verify that the expression being parsed is produced by the grammar but I would like to store the structure in a variable. Where would this be done and do you have any advice for how this might look in pseudo (or even better C++) code.
For completeness, I am using the following JSON as a test.
[
{
"libraries":[
"terminal",
"binary"
] ,
"functions":[
"terminal-basic",
"binary-basic"
]
}
,
{
"name":"addition",
"type":"binary-basic",
"function":"add_float",
"input":{
"float" : 2
},
"output":"float",
"max-number":2
}
,
{
"name":"exponent",
"type":"binary-basic",
"function":"exponent_float",
"input":{
"float":2
},
"output":"float",
"max-number":2
}
,
{
"name":"exponent",
"type":"binary-basic",
"function":"exponent_float",
"input":{
"float":2,
"int":1
},
"output":"float",
"max-number":1
}
,
{
"name":"constant_1",
"type":"terminal-basic",
"function":"non_random_constant",
"value":0.5,
"input":{ },
"output":"float",
"max-number":3
}
,
{
"name":"constant_2",
"type":"terminal-basic",
"function":"non_random_constant",
"value":2.0,
"input":{ },
"output":"float",
"max-number":3
}
,
{
"name":"constant_3",
"type":"terminal-basic",
"function":"non_random_constant",
"value":true,
"input":{
"bool":1
},
"output":"bool",
"max-number":1
}
]
I wouldn't like to leave this question unanswered for anyone coming here in the future, however, I am personally not a big fan of the code that accompanies this answer. It feels inefficient, not particularly elegant and I am unsure if it represents the theoretical model I was trying to implement in the first place. I took my lead from #MSalters comment, which to me meant build something that works and worry if the model is theoretically sound later. Below is my attempt.
The header adds a few more functions. Many of them purely to assist fsm and parser.
class JSONParser
{
public:
JSONParser();
~JSONParser();
void parseFile(std::string);
private:
json_value root;
std::stack<std::string> s;
std::stack<json_value> s_value;
// Lexer
bool checkDeliminator(char);
std::vector<std::string> lexer(std::ifstream &);
// FSM varaibles
enum state { int_value, float_value, bool_value, string_value, default_value, bad_state};
state current;
// FSM
void fsm(std::string);
// Parser variables
enum stack_map { list_open, list_close, object_open, object_close, colon, comma, buffer, follow};
std::map<std::string, stack_map> stack_conversion;
// Parser helper functions
template<typename T> void addElement();
template<typename T> void insert(std::string &, T (*)(const std::string &));
template<typename T> void insert();
void insert(std::string &);
void pushBuffer();
template<typename ... T> bool multiComparision(const char scope, T ... args);
bool isDigit(const char);
static int st2i(const std::string & value);
static float st2f(const std::string & value);
static bool st2b(const std::string & value);
// Parser
void parser(const std::string & cursor);
};
The implementation file follows.
#include "genetic-optimization/JSONParser.h"
JSONParser::JSONParser() {
state current = default_value;
stack_conversion = { { "[", list_open }, { "]", list_close }, { "{", object_open }, { "}", object_close }, { ":", colon }, { ",", comma }, { "buffer", buffer } };
}
JSONParser::~JSONParser() = default;
void JSONParser::parseFile(std::string FILE) {
std::ifstream configfile(FILE);
std::vector<std::string> scan = lexer(configfile);
scan.push_back("terminate");
for (auto it = scan.begin(); it != scan.end(); ++it) {
parser(*it);
}
root = s_value.top();
s_value.pop();
}
// Lexer
bool JSONParser::checkDeliminator(char piece) {
switch (piece) {
case '[':
return true;
case ']':
return true;
case '{':
return true;
case '}':
return true;
case ':':
return true;
case ',':
return true;
default:
return false;
}
}
std::vector<std::string> JSONParser::lexer(std::ifstream & configfile) {
char piece;
std::string capture = "";
std::string conversion;
std::vector<std::string> capture_list;
while(configfile >> piece) {
if (checkDeliminator(piece)) {
conversion = piece;
if (capture != "") {
capture_list.push_back(capture);
capture_list.push_back(conversion);
capture = "";
} else {
capture_list.push_back(conversion);
}
} else {
capture += piece;
}
}
return capture_list;
}
// FSM
void JSONParser::fsm(std::string value) {
current = default_value;
char point;
auto it = value.begin();
while (it != value.end()) {
point = *it;
if (point == '"' & current == default_value) {
current = string_value;
return;
} else if (isdigit(point)) {
if (current == default_value | current == int_value) {
current = int_value;
++it;
} else if (current == float_value) {
++it;
} else {
current = bad_state;
return;
}
} else if (point == '.' & current == int_value) {
current = float_value;
++it;
} else if (point == 'f' & current == float_value) {
++it;
} else if (current == default_value) {
if (value == "true" | value == "false") {
current = bool_value;
return;
} else {
current = bad_state;
return;
}
} else {
current = bad_state;
return;
}
}
}
// Parser Helper functions
template<>
void JSONParser::addElement<jobject>() {
json_value value_read;
json_value key_read;
value_read = s_value.top();
s_value.pop();
key_read = s_value.top();
s_value.pop();
std::get<jobject>(s_value.top()).insert(key_read, value_read);
}
template<>
void JSONParser::addElement<jlist>() {
json_value value_read;
value_read = s_value.top();
s_value.pop();
std::get<jlist>(s_value.top()).push_back(value_read);
}
template<typename T>
void JSONParser::insert(std::string & value, T (*fptr)(const std::string &)) {
T T_value(fptr(value));
s_value.push(T_value);
}
template<typename T>
void JSONParser::insert() {
T T_value;
s_value.push(T_value);
}
void JSONParser::insert(std::string & value) {
value.erase(std::remove(value.begin(), value.end(), '"'), value.end());
s_value.push(value);
}
void JSONParser::pushBuffer() {
s.pop();
s.push("buffer");
}
template<typename ... T>
bool JSONParser::multiComparision(const char scope, T ... args) {
return (scope == (args || ...));
}
bool JSONParser::isDigit(const char c) {
return multiComparision<char>(c, '1', '2', '3', '4', '5', '6', '7', '8', '9', '0');
}
int JSONParser::st2i(const std::string & value) {
return stoi(value);
}
float JSONParser::st2f(const std::string & value) {
return stof(value);
}
bool JSONParser::st2b(const std::string & value) {
if (value == "true") {
return true;
} else {
return false;
}
}
// Parser
void JSONParser::parser(const std::string & cursor) {
if(s.empty()) {
s.push(cursor);
} else {
stack_map stack_value;
std::string value = s.top();
if (stack_conversion.find(value) != stack_conversion.end()) {
stack_value = stack_conversion[s.top()];
} else {
stack_value = follow;
}
switch (stack_value) {
case buffer:
s.pop();
break;
case list_open:
insert<jlist>();
if (cursor == "]") {
pushBuffer();
return;
}
break;
case list_close:
addElement<jlist>();
s.pop();
s.pop();
break;
case object_open:
insert<jobject>();
if (cursor == "}") {
pushBuffer();
return;
}
break;
case object_close:
addElement<jobject>();
s.pop();
s.pop();
break;
case colon:
s.pop();
break;
case comma:
s.pop();
if (s.top() == "{") {
addElement<jobject>();
} else {
addElement<jlist>();
}
break;
default:
s.pop();
fsm(value);
switch (current) {
case string_value:
insert(value);
break;
case int_value:
insert<int>(value, st2i);
break;
case float_value:
insert<float>(value, st2f);
break;
case bool_value:
insert<bool>(value, st2b);
break;
default:
std::cout << "Bad state\n";
}
}
s.push(cursor);
}
}
The idea was to have the lexer break at each deliminator and place all the generated tokens into a vector. This vector called scan could then be looped through. At each iteration of this loop parser would be run. In general this reads the top of the stack s and determines whether a bracket/brace is opening or closing or a terminal value has been reached. If a bracket/brace is opening, a new jobject or jlist is generated and placed onto a new stack s_value, if a terminal value is reached fsm (finite state machine) runs and determines the type of value and places it on top of s_value, should a comma or closing bracket be reached the appropriate values are moved off the stack and the elements in s_value are inserted into their appropriate containers.
The biggest meatball in this spaghetti is how elements in the JSON tree are called.
std::cout << std::get<bool>(std::get<jobject>(std::get<jobject>(std::get<jlist>(root)[6])["input"])["bool"]); // Should return 1
While this does indeed return 1. The nested std::get calls seem just plain wrong and I'm not sure if they can be incorporated into the operator [] or through (sigh) a third stack that tracks the type of object being stored.
This was my basic attempt, it's not pretty but it does work. Hopefully I can refine it further and improve on what I have.
I am not an expert at parsing so my answer would be very heuristic...
JSON grammar is simple. I believe we do not need to try to understand of follow over-specified (E)BNF form to actually parse JSON string. Try to write your own simple form. After you do that, you may feel a need for a better form. Then you can re-try to fully understand why there are such grammars.
Isn't FSM simply "you have to do that in this state?" States are preferably managed by a stack (not like you have to have an instance whose members indicate states like an abstract figure in text book in many cases of the real world) and you will do what you have to do in loops based on a top state of the stack. I believe you do not need an instance of 'parse table.' Can it be abstract or pervasively exists somewhere in code?
I also started to practice parsing with JSON. Please check my single header file.
I used 7 stack statuses:
enum status {
READING_OBJECT_KEY,
READ_OBJECT_KEY,
READING_OBJECT_VALUE, READING_ARRAY_VALUE,
READ_OBJECT_VALUE, READ_ARRAY_VALUE, READ_OTHER_VALUE
};
Heuristically, I started actual parsing after skipping preceding whitespace and check the first non-whitespace character:
} else if (p.c == '{') {
p.ps.push(json::parsing::READING_OBJECT_KEY);
j = json::object();
p.js.push(j.v);
break;
} else if (p.c == '[') {
p.ps.push(json::parsing::READING_ARRAY_VALUE);
j = json::array();
p.js.push(j.v);
break;
}
Then I actually started to parse with 8 functions:
while (p.iss.get(p.c)) {
p.i++;
if (p.c == ' ' ) {}
else if (p.c == '{' ) json::parse__left_brace(p);
else if (p.c == '}' ) json::parse__right_brace(p);
else if (p.c == '[' ) json::parse__left_bracket(p);
else if (p.c == ']' ) json::parse__right_bracket(p);
else if (p.c == ':' ) json::parse__colon(p);
else if (p.c == ',' ) json::parse__comma(p);
else if (p.c == '\"') json::parse__quote(p);
else json::parse__else(p);
}
I am looking for specific Design Pattern in C++ that solve this problem.
I want to design a Storyboard. Our version of the Storyboard
contains arbitrary many notes (imagine it like putting sticky notes on a board).
Every note has a title, a text and a set of tags. E.g.
- title: "Test Traceplayer"
- text: "Implement a unit test for the class Traceplayer of the spark core framework."
- tags: {"unit test", "traceplayer", "testing", "spark core"}
Our Storyboard should enable us to search for notes by title, text and tags.
E.g.:
searchByTitle( "Test Traceplayer" )
searchByTag({"testing", "unit test"})
searchByText("Implement a unit test for the class Traceplayer of the spark core framework.")
For the sake of simplicity we don't want to do any similiarity or prefix matching when
searching for a title, tag or text. Only an exact match should give results.
I have number of solution that solve this problem O(1) search complexity But can any one suggest any "Design Pattern" that solve this problem.
Solve that issue with three STL map and get constant time search complexity
Looking for a specific Design Pattern that solves this problem.
I have solved this problem using 3 STL Map and solution get O(1) search complexity
#include <iostream>
#include <vector>
#include <map>
#define INPUT 8
class Note {
public:
string Tital;
string Text;
vector<string> vec;
Note(){
Tital = "\0";
Text = "\0";
}
};
class storyBoard{
public:
void AddNote(string Tital,string Text,vector<string> vec );
void RemoveByTital(string &tital);
void PrintStoredData();
Note* searchByTitle(string titleSearch);
Note* searchByText(string text_);
vector<Note*> searchByTag(string titleSearch);
void printSlip(Note *tm);
storyBoard(){}
private:
std::map<string,Note *> TitalMap;
std::map<string,Note *> TextMap;
std::map<string,std::vector<Note *> > TagsMap;
};
Note* storyBoard::searchByTitle(string titleSearch){
auto it_v = TitalMap.find(titleSearch);
if (it_v != TitalMap.end()){
cout<< "Tital search result is below:-"<<endl;
return it_v->second;
} else {
cout <<"data "<<titleSearch << " Not found"<<endl;
return NULL;
}
}
Note* storyBoard::searchByText(string titleSearch){
auto it_v = TextMap.find(titleSearch);
if (it_v != TextMap.end()){
cout<< "Text search result is below:-"<<endl;
return it_v->second;
} else {
cout <<"data "<<titleSearch << " Not found"<<endl;
return NULL;
}
}
vector<Note*> storyBoard::searchByTag(string tagSearch){
auto it_v = TagsMap.find(tagSearch);
if (it_v != TagsMap.end()){
cout<< "Tag search result is below:-"<<endl;
return it_v->second;
} else {
cout <<"data "<<tagSearch << " Not found"<<endl;
vector<Note*> del;
return del;
}
}
void storyBoard::AddNote(string Tital, string Text, vector<string> v){
Note *note = new Note;
note->Tital = Tital;
note->Text = Text;
note->vec = v;
TitalMap[note->Tital] = note;
TextMap[note->Text] = note;
for (auto it = note->vec.begin(); it != note->vec.end(); ++it){
//check that is tags already
auto it_v = TagsMap.find(*it);
if (it_v != TagsMap.end()){
it_v->second. push_back(note);
} else {
vector<Note *> &v = TagsMap[*it];
v.push_back(note);
}
}
}
void storyBoard::printSlip(Note *tm){
cout << "Tital=" << tm->Tital <<endl
<< "Text=" << tm->Text <<endl
<< "Tags = ";
for (auto it = tm->vec.begin(); it != tm->vec.end(); ++it){
cout<< *it<<"\t";
}
cout<<endl<<endl;
}
void storyBoard::PrintStoredData(){
for(auto tm : TitalMap){
printSlip(tm.second);
}
cout<<endl;
}
void feed_data_for_testing(storyBoard &Sb);
void TestCase(storyBoard &Sb);
int main() {
storyBoard Sb;
feed_data_for_testing(Sb);
Sb.PrintStoredData(); /*Print all contain data */
cout<<"************* From Here start searching ************"<<endl;
TestCase(Sb);
return 0;
}
void TestCase(storyBoard &Sb){
Note* obj = Sb.searchByTitle("Tital-3");
if(obj != NULL){
Sb.printSlip(obj);
}
obj = Sb.searchByText("Text-4");
if(obj != NULL){
Sb.printSlip(obj);
}
vector<Note *> vec = Sb.searchByTag("tag-3");
if(vec.size() !=0){
for (auto it = vec.begin(); it != vec.end(); ++it){
//cout<<(*it)->Tital << "\t";
Sb.printSlip(*it);
}
}
}
void feed_data_for_testing(storyBoard &Sb){
vector<string> tags ;
int count =INPUT;
for(int i =1;i<=count;i++){
string tital = "Tital-" + std::to_string(i);
string text = "Text-" + std::to_string(i);
tags.clear();
for(int j =1;j<=i;j++){
string tag_ = "tag-" + std::to_string(j);
tags.push_back(tag_);
}
Sb.AddNote(tital,text,tags);
}
}
I am looking for a design pattern that solves this issue.
I update your code at the following point:-
convert the class into singleton so that data maintain a single map for each type
change map to unorder_map
#define INPUT 8
using namespace std;
/*use class to store single slip data*/
class Note {
public:
string Tital;
string Text;
vector<string> vec;
Note(){ //constructor to initialize data zero
Tital = "\0";
Text = "\0";
}
};
/*create a singalton pattern class so that create only one data storage*/
class storyBoard{
public:
static storyBoard* getInstance(){
storyBoard* Instance= instance.load();
if ( !Instance ){
std::lock_guard<std::mutex> myLock(lock);
Instance= instance.load();
if( !Instance ){
Instance= new storyBoard();
instance.store(Instance);
}
}
return Instance;
}
void AddNote(string Tital,string Text,vector<string> vec );
void RemoveByTital(string &tital);
void PrintStoredData();
Note* searchByTitle(string titleSearch);
Note* searchByText(string text_);
vector<Note*> searchByTag(string titleSearch);
void printSlip(Note *tm);
private:
storyBoard()= default;
~storyBoard()= default;
storyBoard(const storyBoard&)= delete;
storyBoard& operator=(const storyBoard&)= delete;
static std::atomic<storyBoard*> instance;
static std::mutex lock;
std::unordered_map<string,Note *> TitalMap;
std::unordered_map<string,Note *> TextMap;
std::unordered_map<string,std::vector<Note *> > TagsMap;
};
std::atomic<storyBoard*> storyBoard::instance;
std::mutex storyBoard::lock;
Note* storyBoard::searchByTitle(string titleSearch){
auto it_v = TitalMap.find(titleSearch);
if (it_v != TitalMap.end()){
cout<< "Tital search result is below:-"<<endl;
return it_v->second;
} else {
cout <<"data "<<titleSearch << " Not found"<<endl;
return NULL;
}
}
Note* storyBoard::searchByText(string titleSearch){
auto it_v = TextMap.find(titleSearch);
if (it_v != TextMap.end()){
cout<< "Text search result is below:-"<<endl;
return it_v->second;
} else {
cout <<"data "<<titleSearch << " Not found"<<endl;
return NULL;
}
}
vector<Note*> storyBoard::searchByTag(string tagSearch){
auto it_v = TagsMap.find(tagSearch);
if (it_v != TagsMap.end()){
cout<< "Tag search result is below:-"<<endl;
return it_v->second;
} else {
cout <<"data "<<tagSearch << " Not found"<<endl;
vector<Note*> del;
return del;
}
}
void storyBoard::AddNote(string Tital, string Text, vector<string> v){
Note *note = new Note;
note->Tital = Tital;
note->Text = Text;
note->vec = v;
TitalMap[note->Tital] = note;
TextMap[note->Text] = note;
for (auto it = note->vec.begin(); it != note->vec.end(); ++it){
//check that is tags already
auto it_v = TagsMap.find(*it);
if (it_v != TagsMap.end()){
it_v->second. push_back(note);
} else {
vector<Note *> &v = TagsMap[*it];
v.push_back(note);
}
}
}
void storyBoard::printSlip(Note *tm){
cout << "Tital=" << tm->Tital <<endl
<< "Text=" << tm->Text <<endl
<< "Tags = ";
for (auto it = tm->vec.begin(); it != tm->vec.end(); ++it){
cout<< *it<<"\t";
}
cout<<endl<<endl;
}
void storyBoard::PrintStoredData(){
for(auto tm : TitalMap){
printSlip(tm.second);
}
cout<<endl;
}
/**temporary function only use for testing*/
void TestCase(){
storyBoard *Sb = storyBoard::getInstance();
Note* obj = Sb->searchByTitle("Tital-3");
if(obj != NULL){
Sb->printSlip(obj);
}
obj = Sb->searchByText("Text-4");
if(obj != NULL){
Sb->printSlip(obj);
}
vector<Note *> vec = Sb->searchByTag("tag-3");
if(vec.size() !=0){
for (auto it = vec.begin(); it != vec.end(); ++it){
//cout<<(*it)->Tital << "\t";
Sb->printSlip(*it);
}
}
}
/**temporary function only use for testing*/
void feed_data_for_testing(){
storyBoard *Sb = storyBoard::getInstance();
vector<string> tags ;
int count =INPUT;
for(int i =1;i<=count;i++){
string tital = "Tital-" + std::to_string(i);
string text = "Text-" + std::to_string(i);
tags.clear();
for(int j =1;j<=i;j++){
string tag_ = "tag-" + std::to_string(j);
tags.push_back(tag_);
}
Sb->AddNote(tital,text,tags);
}
}
int main() {
storyBoard *Sb = storyBoard::getInstance();
feed_data_for_testing();
Sb->PrintStoredData(); /*Print all contain data */
cout<<"************* From Here start searching ************"<<endl;
TestCase();
return 0;
}
I think the term design pattern was mistakenly used by your interviewer in place of the term idiom.
A major issue with your code (and could be the cause of rejection) is memory handling using classical c++ idioms:
use of smart pointers to manage the lifetime of your notes
rule of 3 (or 5) to handle copy/assignment(/move) of your StoryBoard object
Note that using smart pointers is only one way of managing the memory. You could as well use other idioms:
arena of notes and referencing notes from the arena inside your hashmaps
...
Once this issue is solved you have minor issues:
returning pointers instead of references, the StoryBoard is the owner of the memory, you should not return a pointer that a caller could inadvertently free.
no const accessors (returning const references)
repetitive code that could be factored
If I am not mistaken in interpreting what the interviewer said, this question should be moved to codereview.stackexhange.com
I use the JID3 library which works fine for IDv2_3 but when it is asked to update a tracks tags which are V2_4, it simply wipes the Id3v2_4 tags. How can I test which version of tagging a track has.
Example of setting the ratings and times played in the POPULARIMETER tag
public String setmp3RatingTag(Context context, File SourceFile, String email, int rating, int timesPlayed) throws Exception {
String error = null;
if (timesPlayed < 0) {
timesPlayed = 0;
}
try {
MediaFile MediaFile = new MP3File(SourceFile);
ID3V2_3_0Tag ID3V2_3_0Tag = (org.blinkenlights.jid3.v2.ID3V2_3_0Tag) MediaFile.getID3V2Tag();
POPMID3V2Frame popmid3V2Frame = new POPMID3V2Frame(email, rating, timesPlayed);
popmid3V2Frame.setPopularity(email, rating, timesPlayed);
if (ID3V2_3_0Tag != null) {
frames = ID3V2_3_0Tag.getPOPMFrames();
if (frames != null) {
if (frames.length > 0) {
String emailtouser[]=getmp3Email(SourceFile);
for (int i = 0; i < frames.length; i++) {
if (frames[i] != null) {
ID3V2_3_0Tag.removePOPMFrame(emailtouser[i]);
}
}
}
}
} else {
ID3V2_3_0Tag = new ID3V2_3_0Tag();
}
ID3V2_3_0Tag.addPOPMFrame(popmid3V2Frame);
MediaFile.setID3Tag(ID3V2_3_0Tag);
MediaFile.sync();
} catch (ID3Exception | OutOfMemoryError e) {
e.printStackTrace();
error = e.getMessage();
}
error = finish(context, SourceFile);
return error;
}
For anyone using the jid3 library, I added another method to the class MP3File.java
#Override
public int testforVerion() {
int iMinorVersion=0;
try {
InputStream oSourceIS = new BufferedInputStream(m_oFileSource.getInputStream());
ID3DataInputStream oID3DIS = new ID3DataInputStream(oSourceIS);
try
{
// check if v2 tag is present
byte[] abyCheckTag = new byte[3];
oID3DIS.readFully(abyCheckTag);
if ((abyCheckTag[0] == 'I') && (abyCheckTag[1] == 'D') && (abyCheckTag[2] == '3'))
{
// check which version of v2 tags we have
iMinorVersion = oID3DIS.readUnsignedByte();
}
else
{
return iMinorVersion;
}
}
finally
{
oID3DIS.close();
}
} catch (IOException e) {
e.printStackTrace();
}
return iMinorVersion;
}
simply check for value 3 for ID3V2_3 and 4 for ID3V2_4
I have a small python programm that computes a 2d positon
def getPosition(lerpParameter):
A = getClothoidConstant()
...
The python script can be found in my project folder here.
The function getPosition is included in the file clothoid.py. The function returns a value of type Vector2. How can I access the data of the returned Vector2d in my C++ program?
The C++ programm looks like this:
/// Get function
PyObject *pFunc = PyObject_GetAttrString(pModule, pid.functionName);
// pFunc is a new reference
if (pFunc && PyCallable_Check(pFunc))
{
PyObject *pArgs = PyTuple_New(pid.getArgumentCount());
PyObject *pValue = nullptr;
// setup function parameters
for (int i = 0; i < pid.getArgumentCount(); ++i)
{
if (pid.arguments[i].type == eType::Int)
{
pValue = PyLong_FromLong(atoi(pid.arguments[i].name.c_str()));
}
else
{
pValue = PyFloat_FromDouble(atof(pid.arguments[i].name.c_str()));
}
if (!pValue)
{
Py_DECREF(pArgs);
Py_DECREF(pModule);
std::cout << "Cannot convert argument" << std::endl;
throw std::runtime_error("Cannot convert argument");
}
// pValue reference stolen here:
PyTuple_SetItem(pArgs, i, pValue);
}
pValue = PyObject_CallObject(pFunc, pArgs);
Py_DECREF(pArgs);
if (pValue != nullptr)
{
switch (pid.returnType)
{
...
case eType::Double:
//std::cout << "Result of call: " << PyFloat_AsDouble(pValue) << std::endl;
doubleValue_ = PyFloat_AsDouble(pValue);
break;
case eType::Vector2d:
{
// How can I acccess the data here?
}
break;
default:
break;
}
Py_DECREF(pValue);
}
If the return type is a double or int it is easy to get the corresponding value. But I have no idea how to access the data of the Vector2.
If you want to get the 'x' attribute, and you know it's a float:
PyObject *temp = PyObject_GetAttrString(pValue, "x");
if (temp == NULL) {
// error handling
}
double x = PyFloat_AsDouble(temp);
// clean up reference when done with temp
Py_DECREF(temp);
I am working on an application that builds a vector of structs for items in a given directory and returns a reference of the vector for it to be read, I receive the following errors when attempting to compile the example code below:
1. 'class std::vector<indexStruct, std::allocator<indexStruct> >' has no member named 'name'
2. no matching function for call to `std::vector<indexStruct, std::allocator<indexStruct> >::push_back(std::vector<indexStruct, std::allocator<indexStruct> >&)'
exampleApp.cpp
#include "exampleApp.h"
exampleApp::exampleApp()
{
this->makeCatalog();
}
char* findCWD()
{
char* buffer = new char[_MAX_PATH];
return getcwd(buffer, _MAX_PATH);
}
void exampleApp::makeCatalog()
{
char* cwd = this->findCWD();
vector<indexStruct> indexItems;
this->indexDir(cwd, indexItems);
}
void exampleApp:indexDir(char* dirPath, vector<indexStruct>& indexRef)
{
DIR *dirPointer = NULL;
struct dirent *dirItem = NULL;
vector<indexStruct> indexItems;
vector<indexStruct> indexItem;
try
{
if ((dirPointer = opendir(dirPath)) == NULL) throw 1;
while (dirItem = readdir(dirPointer))
{
if (dirItem == NULL) throw 2;
if (dirItem->d_name[0] != '.')
{
indexItem.name = dirItem->d_name;
indexItem.path = dirPath;
indexItems.push_back(indexItem);
indexItem.clear();
}
}
indexRef.swap(indexItems);
closedir(dirPointer);
}
catch(int errorNo)
{
//cout << "Caught Error #" << errorNo;
}
}
exampleApp.h
#ifndef EXAMPLEAPP_H
#define EXAMPLEAPP_H
#include <iostream.h>
#include <dirent.h>
#include <stdlib.h>
#include <vector.h>
using namespace std;
struct indexStruct
{
char* name;
char* path;
};
class exampleApp
{
public:
exampleApp();
private:
char* findCWD();
void makeCatalog();
void indexDir(char* dirPath, vector<indexStruct>& indexRef);
};
#endif
What am I doing wrong here, and is there a better way going about this?
You've made 'indexItem' a vector, you probably just want it to be the type you want to put in 'indexItems'. Also, I'd create the new struct in your loop:
while (dirItem = readdir(dirPointer))
{
if (dirItem == NULL) throw 2;
if (dirItem->d_name[0] != '.')
{
indexStruct indexItem;
indexItem.name = dirItem->d_name;
indexItem.path = dirPath;
indexItems.push_back(indexItem);
}
}
You are defining a vector called indexItem:
vector<indexStruct> indexItem;
This is just an array. So the following lines must be changed to reference a specific element of the vector:
indexItem.name = dirItem->d_name;// should be indexItem[..].name or indexItem.at(..).name
indexItem.path = dirPath; // same as above!