Skip to content

Instantly share code, notes, and snippets.

@furfurylic
Last active April 1, 2024 12:11
Show Gist options
  • Save furfurylic/6eb00239e4ab0c2bd6005e0fa6dab0a3 to your computer and use it in GitHub Desktop.
Save furfurylic/6eb00239e4ab0c2bd6005e0fa6dab0a3 to your computer and use it in GitHub Desktop.
commata - primer
Sample codes in Commata Primer
cmake_minimum_required(VERSION 3.13)
project(commata_primer CXX)
enable_language(CXX)
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_CXX_EXTENSIONS OFF)
set_property(GLOBAL PROPERTY USE_FOLDERS ON)
include(FetchContent)
FetchContent_Declare(
commata
GIT_REPOSITORY https://github.com/furfurylic/commata.git
GIT_TAG v0.2.9
)
FetchContent_MakeAvailable(commata)
add_executable(commata_primer main.cpp)
target_compile_features(commata_primer PRIVATE cxx_std_14)
if(CMAKE_CXX_COMPILER_ID STREQUAL "MSVC")
target_compile_options(commata_primer PRIVATE
/MP /W4 /bigobj
$<$<CONFIG:MinSizeRel>:/wd4702>
$<$<CONFIG:Release>:/wd4702>
$<$<CONFIG:RelWithDebInfo>:/wd4702>
)
elseif(CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
target_compile_definitions(commata_primer PRIVATE
$<$<NOT:$<CONFIG:Debug>>:NDEBUG>
)
target_compile_options(commata_primer PRIVATE
-Wall -Wextra -pedantic-errors -Werror=pedantic
$<$<CONFIG:Debug>:-O0 -g3>
$<$<CONFIG:RelWithDebInfo>:-O2 -g3>
)
elseif(CMAKE_CXX_COMPILER_ID STREQUAL "Clang")
target_compile_definitions(commata_primer PRIVATE
$<$<NOT:$<CONFIG:Debug>>:NDEBUG>
)
target_compile_options(commata_primer PRIVATE
-Wall -Wextra -pedantic-errors -Werror=pedantic
-Wno-gnu-zero-variadic-macro-arguments
$<$<CONFIG:Debug>:-O0 -g3>
$<$<CONFIG:RelWithDebInfo>:-O2 -g3>
)
endif()
target_link_libraries(commata_primer PRIVATE commata)
/**
* These codes are licensed under the Unlicense.
* http://unlicense.org
*/
#include <algorithm>
#include <cctype>
#include <cstring>
#include <deque>
#include <fstream>
#include <iostream>
#include <iterator>
#include <limits>
#include <set>
#include <stdexcept>
#include <string_view>
#include <vector>
#include <commata/field_scanners.hpp>
#include <commata/parse_csv.hpp>
#include <commata/stored_table.hpp>
#include <commata/table_pull.hpp>
#include <commata/table_scanner.hpp>
#include <commata/text_error.hpp>
#include <commata/wrapper_handlers.hpp>
using namespace std::literals;
using commata::make_stored_table_builder;
using commata::parse_csv;
using commata::stored_table;
using commata::wstored_table;
using commata::table_scanner;
using commata::make_empty_physical_line_aware;
using commata::make_field_translator;
using commata::text_error;
using commata::text_error_info;
using commata::replacement_ignore;
using commata::make_csv_source;
using commata::make_table_pull;
using commata::indirect;
void stored_table_sample()
{
// Make an empty stored_table object
stored_table table;
// Open stars.csv and load its content into table
parse_csv(std::ifstream("stars.csv"), make_stored_table_builder(table));
std::cout << table.size() << std::endl; // will print "9" (not "10")
std::cout << table[0][3] << std::endl; // will print "Distance, in parsec"
std::cout << table[1][1] << std::endl; // will print "Spica"
std::cout << table[5][1] << std::endl; // will print "Deneb"
std::cout << table[5][3] << std::endl; // will print "" (an empty string)
std::cout << std::strlen(table[6][1].c_str()) << std::endl;
// will print the length of "Albireo", which is 7
for (char c : table[0][0]) {
std::cout << static_cast<char>(std::toupper(static_cast<unsigned char>(c)));
} // will print "CONSTELLATION"
std::cout << std::endl;
std::cout << (table[1][1] == "Spica") << std::endl;
// table[1][1] is "Spica", so will print "1" or "true" or something like that
std::cout << (table[1][3] < std::to_string(75)) << std::endl;
// table[1][3] is "77", so will print the result of the lexicographical
// comparison of "77" and "75"
table[5][2].erase(3); // table[5][2] will change from "1.25" to "1.2"
table[5][2][2] = '3'; // table[5][2] will change from "1.2" to "1.3"
table.rewrite_value(table[5][3], "430");
// table[5][3] change from "" to "430"
table.content().pop_front(); // erases the first record
std::stable_sort(table.content().begin(), table.content().end(),
[](const auto& record1, const auto& record2) {
return record1[1] < record2[1];
}); // sorts on the names of the stars
table.content().emplace_front(1, table.import_value("Constellation"));
// adds a new record which consists of one value "Constellation"
}
void stored_table_sample2()
{
wstored_table table;
parse_csv(std::wifstream("stars.csv"), make_stored_table_builder(table));
std::wcout << (table[1][0] == L"Virgo") << std::endl;
// will print "1" or "true" or something like that
}
void one_pass_scanning_sample()
{
std::set<std::string> constellation_set;
std::vector<std::string> names;
std::deque<double> magnitudes;
table_scanner scanner(1 /* means that the first one record is a header */);
scanner.set_field_scanner(0, make_field_translator(constellation_set));
// sets a body field scanner for field #0 (zero-based)
scanner.set_field_scanner(1, make_field_translator(names));
// sets a body field scanner for field #1 (zero-based)
scanner.set_field_scanner(2, make_field_translator(magnitudes));
// sets a body field scanner for field #2 (zero-based)
parse_csv(std::ifstream("stars.csv"), std::move(scanner));
std::cout << constellation_set.size() << std::endl;
// will print "2"
std::copy(constellation_set.cbegin(), constellation_set.cend(),
std::ostream_iterator<std::string>(std::cout, " "));
// will print "Cygnus Virgo "
// or something like that
std::cout << std::endl;
std::cout << names.size() << std::endl; // will print "8"
std::cout << names.back() << std::endl; // will print "Fawaris"
std::cout << magnitudes.size() << std::endl; // will print "8"
std::cout << magnitudes[2] << std::endl; // will print "2.74"
// or something like that
}
void one_pass_scanning_sample2()
{
table_scanner scanner(1);
// The body field scanner for field #1 will print the name of stars to the
// standard output
scanner.set_field_scanner(1,
make_field_translator<std::string>( // <std::string> is required
std::ostream_iterator<std::string>(std::cout, "\n")));
// The body field scanner for field #2 will make max_magnitude the magnitude
// of the brightest star (note that brighter stars have smaller magnitudes)
double max_magnitude = std::numeric_limits<double>::max();
scanner.set_field_scanner(2,
make_field_translator<double>( // <double> is required
[&max_magnitude](double magnitude) {
max_magnitude = std::min(max_magnitude, magnitude);
}));
parse_csv(std::ifstream("stars.csv"), std::move(scanner));
std::cout << max_magnitude << std::endl; // will print "0.97"
// or something like that
}
void one_pass_scanning_sample3()
{
std::vector<std::string> names;
table_scanner scanner(
[&names, names_attached = false]
(std::size_t field_index,
std::optional<std::pair<const char*, const char*>> field_value,
table_scanner& scanner) mutable {
if (field_value) {
// The value of field_index-th (zero-based) header field is notified,
// whose value is [field_value.first, field_value.end), and
// field_value.end is dereferenceable and points the terminating zero
if (std::string_view(field_value->first,
field_value->second - field_value->first)
== "Name") {
scanner.set_field_scanner(
field_index, make_field_translator(names));
names_attached = true;
} else {
return true; // true to instruct the table scanner to continue to
// report the header fields
}
} else {
// An end of a header record is notified
if (!names_attached) {
throw std::runtime_error(
"Cannot find a field of the names of the stars");
}
}
return false; // false to tell the scanner to uninstall this header field
// scanner, and tell it that here the header records end
// and the next record will be the first body record
});
parse_csv(std::ifstream("stars.csv"), std::move(scanner));
std::cout << names.front() << std::endl; // will print "Spica"
std::cout << names[4] << std::endl; // will print "Deneb"
}
void stored_table_error_sample()
{
stored_table table;
try {
parse_csv(std::ifstream("stars2.csv"), make_stored_table_builder(table));
} catch (const text_error& e) {
std::cout << e.what() << std::endl;
//throw;
}
}
void stored_table_error_sample2()
{
stored_table table;
try {
parse_csv(std::ifstream("stars2.csv"), make_stored_table_builder(table));
} catch (const text_error& e) {
std::cout << text_error_info(e) << std::endl;
//throw;
}
}
void one_pass_scanning_error_sample()
{
double distance_sum = 0.0;
std::size_t distance_num = 0;
table_scanner scanner(1);
scanner.set_field_scanner(3,
make_field_translator<double>(
[&distance_sum, &distance_num](double distance) {
distance_sum += distance;
++distance_num;
}));
try {
parse_csv(std::ifstream("stars.csv"), std::move(scanner));
} catch (const commata::text_error& e) {
std::cout << text_error_info(e) << std::endl;
}
std::cout << distance_sum / distance_num << std::endl;
// will print the average distance
}
void one_pass_scanning_error_sample2()
{
double distance_sum = 0.0;
std::size_t distance_num = 0;
table_scanner scanner(1);
scanner.set_field_scanner(3,
make_field_translator<double>(
[&distance_sum, &distance_num](double distance) {
distance_sum += distance;
++distance_num;
},
replacement_ignore, /* added (1) */
replacement_ignore)); /* added (2) */
parse_csv(std::ifstream("stars.csv"), std::move(scanner));
std::cout << distance_sum / distance_num << std::endl;
// will print the average distance
}
// A table handler type whose objects make a vector of vectors of field values
class vov_table_handler // vov means "vector of vector"
{
std::vector<std::vector<std::string>>* records_;
std::string current_value_;
public:
using char_type = const char;
explicit vov_table_handler(
std::vector<std::vector<std::string>>& records) :
records_(&records)
{}
void start_record(const char*)
{
records_->emplace_back();
}
void update(const char* first, const char* last)
{
// Append [first, last) to the current field value
current_value_.append(first, last);
}
void finalize(const char* first, const char* last)
{
// Append [first, last) to the current field value
// as the final chunk of the value
current_value_.append(first, last);
records_->back().push_back(std::move(current_value_));
current_value_.clear();
}
void end_record(char*)
{}
};
void vov_table_handler_sample()
{
std::vector<std::vector<std::string>> records;
parse_csv(std::ifstream("stars.csv"), vov_table_handler(records));
std::cout << records.size() << std::endl; // will print "9"
std::cout << records[3][1] << std::endl; // will print "Porrima"
}
void make_empty_physical_line_aware_sample()
{
stored_table table;
parse_csv(std::ifstream("stars.csv"),
make_empty_physical_line_aware(make_stored_table_builder(table)));
std::cout << table.size() << std::endl; // will print "10" (not "9")
std::cout << table[5].size() << std::endl; // will print "0"
std::cout << table[6][1] << std::endl; // will print "Deneb"
}
void pull_parsing_sample()
{
auto p = make_table_pull(make_csv_source(std::ifstream("stars.csv")));
p.set_empty_physical_line_aware(true);
// Skip the first record
p.skip_record();
// Skip one field and point the next field
p(1);
std::cout << *p << std::endl; // will print "Spica"
std::cout << p->size() << std::endl; // will print "Spica"'s
// length
std::cout << *p.skip_record(5)(1) << std::endl; // will print "Albireo"
}
void pull_parsing_sample2()
{
auto p = make_table_pull(make_csv_source(std::ifstream("stars.csv")));
std::vector<std::string> v;
while (p.skip_record(), p(1)) { // moves to the end of the record and
// skip the first field of the next record
v.emplace_back(*p); // *p is a reference to std::string_view
// object of the current field value
}
std::cout << v[0] << std::endl; // will print "Spica"
std::cout << v[1] << std::endl; // will print "Zavijava"
std::cout << v[7] << std::endl; // will print "Fawaris"
}
std::string slurp(const char* s)
{
std::filebuf in;
in.open(s, std::ios::in | std::ios::binary);
if (!in.is_open()) {
throw std::runtime_error("Cannot open "s + s);
}
return std::string(std::istreambuf_iterator<char>(&in),
std::istreambuf_iterator<char>());
}
void pull_parsing_sample3()
{
const std::string contents = slurp("stars.csv");
auto p = make_table_pull(make_csv_source(indirect, contents));
static_assert(!std::is_const_v<decltype(p)::char_type>);
while (p.skip_record()(1)) {
std::puts(p.c_str()); // will print stars' names
}
}
int main()
{
stored_table_sample();
std::cout << "-----\n";
stored_table_sample2();
std::cout << "-----\n";
one_pass_scanning_sample();
std::cout << "-----\n";
one_pass_scanning_sample2();
std::cout << "-----\n";
one_pass_scanning_sample3();
std::cout << "-----\n";
stored_table_error_sample();
std::cout << "-----\n";
stored_table_error_sample2();
std::cout << "-----\n";
one_pass_scanning_error_sample();
std::cout << "-----\n";
one_pass_scanning_error_sample2();
std::cout << "-----\n";
make_empty_physical_line_aware_sample();
std::cout << "-----\n";
pull_parsing_sample();
std::cout << "-----\n";
pull_parsing_sample2();
std::cout << "-----\n";
pull_parsing_sample3();
std::cout << "-----\n";
}
Constellation Name Apparent magnitude Distance, in parsec
Virgo Spica 0.97 77
Virgo Zavijava 3.60 11
Virgo Porrima 2.74 38
Virgo Minelauva 3.38 71
Cygnus Deneb 1.25
Cygnus Albireo 3.08 133
Cygnus Sadr 2.23 560
Cygnus Fawaris 2.89 51
We can make this file beautiful and searchable if this error is corrected: Illegal quoting in line 2.
Constellation,Name,Apparent magnitude,"Distance, in parsec"
Virgo,Spica,0.97,77"
Virgo,Zavijava,3.60,11
Virgo,Porrima,2.74,38
Virgo,Minelauva,3.38,71
Cygnus,Deneb,1.25,
Cygnus,Albireo,3.08,133
Cygnus,Sadr,2.23,560
Cygnus,Fawaris,2.89,51
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment