Skip to content

Commit

Permalink
Make C++ file io more idiomatic
Browse files Browse the repository at this point in the history
  • Loading branch information
LivInTheLookingGlass committed Aug 16, 2024
1 parent f6b1377 commit 9f953cc
Show file tree
Hide file tree
Showing 5 changed files with 39 additions and 83 deletions.
109 changes: 32 additions & 77 deletions cplusplus/src/include/utils.hpp
Original file line number Diff line number Diff line change
@@ -1,105 +1,60 @@
#pragma once
#include <stdlib.h>

#include <cerrno>
#include <stdexcept>
#include <stdint.h>
#include <inttypes.h>
#include <string.h>
#include <string>
#include <sstream>
#include <fstream>

#ifdef _WIN32
#include <direct.h>
#include <windows.h>
#define PATH_SEPARATOR "\\"
#else
#include <libgen.h>
#include <stdio.h>
#include <unistd.h>
#define PATH_SEPARATOR "/"
#endif

char* get_parent_directory(char* path, const uint32_t levels) {
#ifdef _WIN32
static char drive[_MAX_DRIVE];
static char dir[_MAX_DIR];
_splitpath(path, drive, dir, NULL, NULL);
for (uint32_t i = 0; i < levels; ++i) {
size_t len = strlen(dir);
if (len > 1 && (dir[len - 1] == '\\' || dir[len - 1] == '/')) {
dir[len - 1] = '\0';
}
char* last_slash = strrchr(dir, '\\');
if (!last_slash) last_slash = strrchr(dir, '/');
if (last_slash) *last_slash = '\0';
}
static char parent_dir[_MAX_PATH];
snprintf(parent_dir, sizeof(parent_dir), "%s%s", drive, dir);
return parent_dir;
#else
char* dir = dirname(path);
#ifndef MAX_PATH
#define MAX_PATH 4096
#endif

std::string get_parent_directory(const std::string &path, const uint32_t levels) {
std::string dir = path;
for (uint32_t i = 0; i < levels; ++i) {
dir = dirname(dir);
if (strcmp(dir, "/") == 0 || strcmp(dir, ".") == 0) {
size_t pos = dir.find_last_of(PATH_SEPARATOR);
if (pos != std::string::npos) {
dir.erase(pos);
} else {
break;
}
}
return dir;
#endif
}

std::string get_data_file(const char *name) {
const char* start = __FILE__;
#ifdef _WIN32
std::string get_data_file(const std::string &name) {
char absolute_path[MAX_PATH];
if (!_fullpath(absolute_path, start, MAX_PATH)) {
perror("_fullpath");
return NULL;
#ifdef _WIN32
if (!_fullpath(absolute_path, __FILE__, sizeof(absolute_path))) {
throw std::runtime_error("_fullpath failed with error code " + std::to_string(GetLastError()));
}
#else
char* absolute_path = realpath(start, NULL);
if (!absolute_path) {
perror("realpath");
return NULL;
if (!realpath(__FILE__, absolute_path)) {
throw std::runtime_error("realpath failed with error code " + std::to_string(errno));
}
#endif
char* parents_dir = get_parent_directory(absolute_path, 3);
const size_t p_len = strlen(parents_dir),
name_len = strlen(name);
char *file_path = (char *)malloc(p_len + name_len + 8);
memcpy(file_path, parents_dir, p_len);
memcpy(file_path + p_len, PATH_SEPARATOR "_data" PATH_SEPARATOR, 7);
memcpy(file_path + p_len + 7, name, name_len);
file_path[p_len + name_len + 7] = 0;
FILE* file = fopen(file_path, "r");
#ifndef _WIN32
free(absolute_path);
#endif
// free(parents_dir);
free(file_path);
if (!file) {
perror("fopen");
return NULL;
}

fseek(file, 0, SEEK_END);
size_t length = ftell(file);
fseek(file, 0, SEEK_SET);

char* buffer = (char*)malloc(length + 1);
if (!buffer) {
perror("malloc");
fclose(file);
return NULL;
std::string file_path = get_parent_directory(std::string(absolute_path), 4) + PATH_SEPARATOR "_data" PATH_SEPARATOR + name;
std::ifstream file(file_path.c_str(), std::ios::in | std::ios::binary);
if (!file) {
throw std::runtime_error("Failed to open file: " + file_path + " with error code " + std::to_string(errno));
}

const size_t ret_code = fread(buffer, 1, length, file);
if (ret_code != length) {
if (feof(file))
printf("Error reading %s: unexpected end of file, read %" PRIu64 " of %" PRIu64 " bytes expected\n", name, (uint64_t)ret_code, (uint64_t)length);
else if (ferror(file))
perror("Error reading data file");
std::ostringstream content_stream;
content_stream << file.rdbuf();
if (!file) {
throw std::runtime_error("Error reading file: " + file_path + " with error code " + std::to_string(errno));
}

buffer[length] = 0;
fclose(file);

std::string ret(buffer);
free(buffer);
return ret;
return content_stream.str();
}
3 changes: 2 additions & 1 deletion docs/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -227,8 +227,9 @@ def setup(app):
labels = [lang[0] for lang in langs]
sizes = [float(lang[1]) for lang in langs]
counts = [countfiles(lang) for lang in labels]
size = max(10, len(labels))
colormap = plt.get_cmap('tab10' if len(labels) <= 10 else 'tab20')
colors = [colormap(idx / len(labels)) for idx, _ in enumerate(labels)]
colors = [colormap(idx / size) for idx, _ in enumerate(labels)]
_, ax = plt.subplots()
ax.pie(sizes, labels=labels, colors=colors, autopct='%1.1f%%', labeldistance=None, pctdistance=0.85)
plt.legend(title='Languages', loc='right', bbox_to_anchor=(1,0.5), bbox_transform=plt.gcf().transFigure)
Expand Down
2 changes: 1 addition & 1 deletion docs/src/cplusplus/lib/macros.rst
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,6 @@ View source code :source:`cplusplus/src/include/macros.hpp`
.. c:namespace-pop::
.. literalinclude:: ../../../../cplusplus/src/include/macros.h
.. literalinclude:: ../../../../cplusplus/src/include/macros.hpp
:language: C++
:linenos:
2 changes: 1 addition & 1 deletion docs/src/cplusplus/lib/math.rst
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,6 @@ Includes
.. c:namespace-pop::
.. literalinclude:: ../../../../cplusplus/src/include/math.h
.. literalinclude:: ../../../../cplusplus/src/include/math.hpp
:language: C++
:linenos:
6 changes: 3 additions & 3 deletions docs/src/cplusplus/lib/utils.rst
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,15 @@ View source code :source:`cplusplus/src/include/utils.hpp`

.. cpp:namespace-push:: utils
.. cpp:function:: std::string get_data_file(const char *name)
.. cpp:function:: std::string get_data_file(const std::string name)

Return a character array containing the whole contents of a file found in _data.

.. cpp:function:: char *get_parent_directory(const char *name, const uint32_t levels)
.. cpp:function:: std::string get_parent_directory(const std::string &path, uint32_t levels)

.. cpp:namespace-pop::

.. literalinclude:: ../../../../cplusplus/src/include/utils.h
.. literalinclude:: ../../../../cplusplus/src/include/utils.hpp
:language: C++
:linenos:

Expand Down

0 comments on commit 9f953cc

Please sign in to comment.