1
0

Add C/C++ template

This commit is contained in:
Athanasios Xygkis 2020-09-14 08:55:52 +02:00
parent cd0392d6d2
commit d52d4fc3a9
12 changed files with 667 additions and 0 deletions

82
template_cpp/.gitignore vendored Normal file
View File

@ -0,0 +1,82 @@
# Created by https://www.toptal.com/developers/gitignore/api/c,c++
# Edit at https://www.toptal.com/developers/gitignore?templates=c,c++
#
bin/da_proc
target/
### C ###
# Prerequisites
*.d
# Object files
*.o
*.ko
*.obj
*.elf
# Linker output
*.ilk
*.map
*.exp
# Precompiled Headers
*.gch
*.pch
# Libraries
*.lib
*.a
*.la
*.lo
# Shared objects (inc. Windows DLLs)
*.dll
*.so
*.so.*
*.dylib
# Executables
*.exe
*.out
*.app
*.i*86
*.x86_64
*.hex
# Debug files
*.dSYM/
*.su
*.idb
*.pdb
# Kernel Module Compile Results
*.mod*
*.cmd
.tmp_versions/
modules.order
Module.symvers
Mkfile.old
dkms.conf
### C++ ###
# Prerequisites
# Compiled Object files
*.slo
# Precompiled Headers
# Compiled Dynamic libraries
# Fortran module files
*.mod
*.smod
# Compiled Static libraries
*.lai
# Executables
# End of https://www.toptal.com/developers/gitignore/api/c,c++

View File

@ -0,0 +1,72 @@
cmake_minimum_required(VERSION 3.9)
project(da_project)
set(CMAKE_C_STANDARD 11)
set(CMAKE_C_STANDARD_REQUIRED ON)
set(CMAKE_C_EXTENSIONS OFF)
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_CXX_EXTENSIONS OFF)
string(CONCAT CMAKE_CXX_FLAGS_COMMON_STR ""
"-Werror -Wall -Wconversion -Wfloat-equal "
"-Wpedantic -Wpointer-arith -Wswitch-default "
"-Wpacked -Wextra -Winvalid-pch "
"-Wmissing-field-initializers "
"-Wunreachable-code -Wcast-align -Wcast-qual "
"-Wdisabled-optimization -Wformat=2 "
"-Wformat-nonliteral -Wuninitialized "
"-Wformat-security -Wformat-y2k -Winit-self "
"-Wmissing-declarations -Wmissing-include-dirs "
"-Wredundant-decls -Wstrict-overflow=5 -Wundef "
"-Wno-unused -Wctor-dtor-privacy -Wsign-promo "
"-Woverloaded-virtual -Wold-style-cast")
if (CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
string(CONCAT CMAKE_CXX_FLAGS_STR "${CMAKE_CXX_FLAGS_COMMON_STR} "
"-Wlogical-op -Wstrict-null-sentinel -Wnoexcept")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS_STR}")
elseif (CMAKE_CXX_COMPILER_ID MATCHES "Clang")
string(CONCAT CMAKE_CXX_FLAGS_STR "${CMAKE_CXX_FLAGS_COMMON_STR} ")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS_STR}")
endif()
string(CONCAT CMAKE_C_FLAGS_COMMON_STR ""
"-Werror -Wall -Wconversion -Wfloat-equal "
"-Wpedantic -Wpointer-arith -Wswitch-default "
"-Wpacked -Wextra -Winvalid-pch "
"-Wmissing-field-initializers -Wunreachable-code "
"-Wcast-align -Wcast-qual -Wdisabled-optimization "
"-Wformat=2 -Wformat-nonliteral -Wuninitialized "
"-Wformat-security -Wformat-y2k -Winit-self "
"-Wmissing-declarations -Wmissing-include-dirs "
"-Wredundant-decls -Wstrict-overflow=5 "
"-Wundef -Wno-unused")
if (CMAKE_C_COMPILER_ID STREQUAL "GNU")
string(CONCAT CMAKE_C_FLAGS_STR "${CMAKE_C_FLAGS_COMMON_STR} "
"-Wlogical-op")
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS_STR}")
elseif (CMAKE_C_COMPILER_ID MATCHES "Clang")
string(CONCAT CMAKE_C_FLAGS_STR "${CMAKE_C_FLAGS_COMMON_STR} ")
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS_STR}")
endif()
set(CMAKE_C_FLAGS_DEBUG "-Winline -g")
set(CMAKE_C_FLAGS_RELEASE "-O3 -DNDEBUG")
set(CMAKE_C_FLAGS_RELWITHDEBINFO "-O2 -g -DNDEBUG")
set(CMAKE_CXX_FLAGS_MINSIZEREL "-Os -DNDEBUG")
set(CMAKE_CXX_FLAGS_DEBUG "-Winline -g")
set(CMAKE_CXX_FLAGS_RELEASE "-O3 -DNDEBUG")
set(CMAKE_CXX_FLAGS_RELWITHDEBINFO "-O2 -g -DNDEBUG")
set(CMAKE_CXX_FLAGS_MINSIZEREL "-Os -DNDEBUG")
# MESSAGE( STATUS "CMAKE_C_FLAGS: " ${CMAKE_C_FLAGS} )
# MESSAGE( STATUS "CMAKE_CXX_FLAGS: " ${CMAKE_CXX_FLAGS} )
# MESSAGE( STATUS "CMAKE_BUILD_TYPE: " ${CMAKE_BUILD_TYPE} )
add_subdirectory(src)

1
template_cpp/bin/README Normal file
View File

@ -0,0 +1 @@
This is a reserved directory name! Store the binary generated by `build.sh` in this directory

View File

@ -0,0 +1 @@
This is a reserved directory name, do not delete or use in your application!

View File

@ -0,0 +1 @@
This is a reserved directory name, do not delete or use in your application!

13
template_cpp/build.sh Executable file
View File

@ -0,0 +1,13 @@
#!/bin/bash
set -e
# Change the current working directory to the location of the present file
cd "$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )"
rm -rf target
mkdir target
cd target
cmake -DCMAKE_BUILD_TYPE=Release ..
cmake --build .
mv src/da_proc ../bin

7
template_cpp/cleanup.sh Executable file
View File

@ -0,0 +1,7 @@
#!/bin/bash
# Change the current working directory to the location of the present file
DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )"
rm -f "$DIR"/bin/da_proc
rm -rf "$DIR"/target

9
template_cpp/run.sh Executable file
View File

@ -0,0 +1,9 @@
#!/bin/bash
# Change the current working directory to the location of the present file
DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )"
ret=0
exec 3>&1; $("$DIR"/bin/da_proc "$@" >&3); ret=$?; exec 3>&-
exit $ret

View File

@ -0,0 +1,7 @@
# DO NAME THE SYMBOLIC VARIABLE `SOURCES`
include_directories(include)
set(SOURCES src/main.cpp)
# DO NOT EDIT THE FOLLOWING LINE
add_executable(da_proc ${SOURCES})

View File

@ -0,0 +1,32 @@
#pragma once
#include "parser.hpp"
void waitOnBarrier(Parser::Host const &barrier);
void waitOnBarrier(Parser::Host const &barrier) {
struct sockaddr_in server;
std::memset(&server, 0, sizeof(server));
int fd = socket(AF_INET, SOCK_STREAM, 0);
if (fd < 0) {
throw std::runtime_error("Could not create the barrier socket: " +
std::string(std::strerror(errno)));
}
server.sin_family = AF_INET;
server.sin_addr.s_addr = barrier.ip;
server.sin_port = barrier.port;
if (connect(fd, reinterpret_cast<struct sockaddr *>(&server),
sizeof(server)) < 0) {
throw std::runtime_error("Could not connect to the barrier: " +
std::string(std::strerror(errno)));
}
char dummy;
if (recv(fd, &dummy, sizeof(dummy), 0) < 0) {
throw std::runtime_error("Could not read from the barrier socket: " +
std::string(std::strerror(errno)));
}
close(fd);
}

View File

@ -0,0 +1,357 @@
#pragma once
#include <fstream>
#include <iostream>
#include <sstream>
#include <string>
#include <vector>
#include <algorithm>
#include <cctype>
#include <locale>
#include <arpa/inet.h>
#include <netdb.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <cstdlib>
#include <cstring>
#include <unistd.h>
class Parser {
public:
struct Host {
Host() {}
Host(size_t id, std::string &ip_or_hostname, unsigned short port)
: id{id}, port{htons(port)} {
if (isValidIpAddress(ip_or_hostname.c_str())) {
ip = inet_addr(ip_or_hostname.c_str());
} else {
ip = ipLookup(ip_or_hostname.c_str());
}
}
std::string ipReadable() const {
in_addr tmp_ip;
tmp_ip.s_addr = ip;
return std::string(inet_ntoa(tmp_ip));
}
unsigned short portReadable() const { return ntohs(port); }
unsigned long id;
in_addr_t ip;
unsigned short port;
private:
bool isValidIpAddress(const char *ipAddress) {
struct sockaddr_in sa;
int result = inet_pton(AF_INET, ipAddress, &(sa.sin_addr));
return result != 0;
}
in_addr_t ipLookup(const char *host) {
struct addrinfo hints, *res;
char addrstr[128];
void *ptr;
memset(&hints, 0, sizeof(hints));
hints.ai_family = PF_UNSPEC;
hints.ai_socktype = SOCK_STREAM;
hints.ai_flags |= AI_CANONNAME;
if (getaddrinfo(host, NULL, &hints, &res) != 0) {
throw std::runtime_error(
"Could not resolve host `" + std::string(host) +
"` to IP: " + std::string(std::strerror(errno)));
}
while (res) {
inet_ntop(res->ai_family, res->ai_addr->sa_data, addrstr, 128);
switch (res->ai_family) {
case AF_INET:
ptr =
&(reinterpret_cast<struct sockaddr_in *>(res->ai_addr))->sin_addr;
inet_ntop(res->ai_family, ptr, addrstr, 128);
return inet_addr(addrstr);
break;
// case AF_INET6:
// ptr = &((struct sockaddr_in6 *) res->ai_addr)->sin6_addr;
// break;
default:
break;
}
res = res->ai_next;
}
throw std::runtime_error("No host resolves to IPv4");
}
};
public:
Parser(const int argc, char const *const *argv, bool withConfig)
: argc{argc}, argv{argv}, withConfig{withConfig}, parsed{false} {}
void parse() {
if (!parseInternal()) {
help(argc, argv);
}
parsed = true;
}
unsigned long id() const {
checkParsed();
return id_;
}
const char *hostsPath() const {
checkParsed();
return hostsPath_.c_str();
}
Host barrier() const {
checkParsed();
return barrier_;
}
const char *outputPath() const {
checkParsed();
return outputPath_.c_str();
}
const char *configPath() const {
checkParsed();
if (!withConfig) {
throw std::runtime_error("Parser is configure to ignore the config path");
}
return configPath_.c_str();
}
std::vector<Host> hosts() {
std::ifstream hostsFile(hostsPath());
std::vector<Host> hosts;
if (!hostsFile.is_open()) {
std::ostringstream os;
os << "`" << hostsPath() << "` does not exist.";
throw std::invalid_argument(os.str());
}
std::string line;
int lineNum = 0;
while (std::getline(hostsFile, line)) {
lineNum += 1;
std::istringstream iss(line);
trim(line);
if (line.empty()) {
continue;
}
unsigned long id;
std::string ip;
unsigned short port;
if (!(iss >> id >> ip >> port)) {
std::ostringstream os;
os << "Parsing for `" << hostsPath() << "` failed at line " << lineNum;
throw std::invalid_argument(os.str());
}
hosts.push_back(Host(id, ip, port));
}
if (hosts.size() < 2UL) {
std::ostringstream os;
os << "`" << hostsPath() << "` must contain at least two hosts";
throw std::invalid_argument(os.str());
}
auto comp = [](const Host &x, const Host &y) { return x.id < y.id; };
auto result = std::minmax_element(hosts.begin(), hosts.end(), comp);
size_t minID = (*result.first).id;
size_t maxID = (*result.second).id;
if (minID != 1UL || maxID != static_cast<unsigned long>(hosts.size())) {
std::ostringstream os;
os << "In `" << hostsPath()
<< "` IDs of processes have to start from 1 and be compact";
throw std::invalid_argument(os.str());
}
std::sort(hosts.begin(), hosts.end(),
[](const Host &a, const Host &b) -> bool { return a.id < b.id; });
return hosts;
}
private:
bool parseInternal() {
if (!parseID()) {
return false;
}
if (!parseHostPath()) {
return false;
}
if (!parseBarrier()) {
return false;
}
if (!parseOutputPath()) {
return false;
}
if (!parseConfigPath()) {
return false;
}
return true;
}
void help(const int, char const *const *argv) {
auto configStr = "CONFIG";
std::cerr << "Usage: " << argv[0]
<< " --id ID --hosts HOSTS --barrier NAME:PORT --output OUTPUT";
if (!withConfig) {
std::cerr << "\n";
} else {
std::cerr << " CONFIG\n";
}
exit(EXIT_FAILURE);
}
bool parseID() {
if (argc < 3) {
return false;
}
if (std::strcmp(argv[1], "--id") == 0) {
if (isPositiveNumber(argv[2])) {
try {
id_ = std::stoul(argv[2]);
} catch (std::invalid_argument const &e) {
return false;
} catch (std::out_of_range const &e) {
return false;
}
return true;
}
}
return false;
}
bool parseHostPath() {
if (argc < 5) {
return false;
}
if (std::strcmp(argv[3], "--hosts") == 0) {
hostsPath_ = std::string(argv[4]);
return true;
}
return false;
}
bool parseBarrier() {
if (argc < 7) {
return false;
}
if (std::strcmp(argv[5], "--barrier") == 0) {
std::string barrier_addr = argv[6];
std::replace(barrier_addr.begin(), barrier_addr.end(), ':', ' ');
std::stringstream ss(barrier_addr);
std::string barrier_name;
unsigned short barrier_port;
ss >> barrier_name;
ss >> barrier_port;
barrier_ = Host(0, barrier_name, barrier_port);
return true;
}
return false;
}
bool parseOutputPath() {
if (argc < 9) {
return false;
}
if (std::strcmp(argv[7], "--output") == 0) {
outputPath_ = std::string(argv[8]);
return true;
}
return false;
}
bool parseConfigPath() {
if (!withConfig) {
return true;
}
if (argc < 10) {
return false;
}
configPath_ = std::string(argv[9]);
return true;
}
bool isPositiveNumber(const std::string &s) const {
return !s.empty() && std::find_if(s.begin(), s.end(), [](unsigned char c) {
return !std::isdigit(c);
}) == s.end();
}
void checkParsed() const {
if (!parsed) {
throw std::runtime_error("Invoke parse() first");
}
}
void ltrim(std::string &s) {
s.erase(s.begin(), std::find_if(s.begin(), s.end(),
[](int ch) { return !std::isspace(ch); }));
}
void rtrim(std::string &s) {
s.erase(std::find_if(s.rbegin(), s.rend(),
[](int ch) { return !std::isspace(ch); })
.base(),
s.end());
}
void trim(std::string &s) {
ltrim(s);
rtrim(s);
}
private:
const int argc;
char const *const *argv;
bool withConfig;
bool parsed;
unsigned long id_;
std::string hostsPath_;
Host barrier_;
std::string outputPath_;
std::string configPath_;
};

View File

@ -0,0 +1,85 @@
#include <chrono>
#include <iostream>
#include <thread>
#include "barrier.hpp"
#include "parser.hpp"
#include <signal.h>
static void stop(int) {
// reset signal handlers to default
signal(SIGTERM, SIG_DFL);
signal(SIGINT, SIG_DFL);
// immediately stop network packet processing
std::cout << "Immediately stopping network packet processing.\n";
// write/flush output file if necessary
std::cout << "Writing output.\n";
// exit directly from signal handler
exit(0);
}
int main(int argc, char **argv) {
signal(SIGTERM, stop);
signal(SIGINT, stop);
// `true` means that a config file is required.
// Call with `false` if no config file is necessary.
bool requireConfig = true;
Parser parser(argc, argv, requireConfig);
parser.parse();
std::cout << "My PID: " << getpid() << "\n";
std::cout << "Use `kill -SIGINT " << getpid() << "` or `kill -SIGTERM "
<< getpid() << "` to stop processing packets\n\n";
std::cout << "My ID: " << parser.id() << "\n\n";
std::cout << "Path to hosts:\n";
std::cout << "==============\n";
std::cout << parser.hostsPath() << "\n\n";
std::cout << "List of resolved hosts is:\n";
std::cout << "==========================\n";
auto hosts = parser.hosts();
for (auto &host : hosts) {
std::cout << host.id << "\n";
std::cout << "Human-readable IP: " << host.ipReadable() << "\n";
std::cout << "Machine-readable IP: " << host.ip << "\n";
std::cout << "Human-readbale Port: " << host.portReadable() << "\n";
std::cout << "Machine-readbale Port: " << host.port << "\n";
std::cout << "\n";
}
std::cout << "\n";
std::cout << "Barrier:\n";
std::cout << "========\n";
auto barrier = parser.barrier();
std::cout << "Human-readable IP: " << barrier.ipReadable() << "\n";
std::cout << "Machine-readable IP: " << barrier.ip << "\n";
std::cout << "Human-readbale Port: " << barrier.portReadable() << "\n";
std::cout << "Machine-readbale Port: " << barrier.port << "\n";
std::cout << "\n";
std::cout << "Path to output:\n";
std::cout << "===============\n";
std::cout << parser.outputPath() << "\n\n";
if (requireConfig) {
std::cout << "Path to config:\n";
std::cout << "===============\n";
std::cout << parser.configPath() << "\n\n";
}
std::cout << "Doing some initialization...\n\n";
std::cout << "Waiting for all processes to finish initialization\n\n";
waitOnBarrier(barrier);
std::cout << "Broadcasting messages...\n\n";
return 0;
}