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 5a5593d commit a34c236
Show file tree
Hide file tree
Showing 5 changed files with 43 additions and 103 deletions.
132 changes: 36 additions & 96 deletions cplusplus/src/include/utils.hpp
Original file line number Diff line number Diff line change
@@ -1,105 +1,45 @@
#pragma once
#include <stdlib.h>
#include <stdint.h>
#include <inttypes.h>
#include <string.h>
#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);
for (uint32_t i = 0; i < levels; ++i) {
dir = dirname(dir);
if (strcmp(dir, "/") == 0 || strcmp(dir, ".") == 0) {
break;
}
}
return dir;
#endif
}
#include <cstdint>
#include <filesystem>
#include <fstream>
#include <iostream>
#include <stdexcept>
#include <string>
#include <vector>

std::string get_data_file(const char *name) {
const char* start = __FILE__;
#ifdef _WIN32
char absolute_path[MAX_PATH];
if (!_fullpath(absolute_path, start, MAX_PATH)) {
perror("_fullpath");
return NULL;
}
#else
char* absolute_path = realpath(start, NULL);
if (!absolute_path) {
perror("realpath");
return NULL;
}
#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;
}
namespace FileUtils {
std::filesystem::path FileUtils::get_parent_directory(const std::filesystem::path& path, uint32_t levels);
std::string get_data_file(const std::string& name);
};

fseek(file, 0, SEEK_END);
size_t length = ftell(file);
fseek(file, 0, SEEK_SET);
inline
std::filesystem::path FileUtils::get_parent_directory(const std::filesystem::path& path, uint32_t levels) {
std::filesystem::path p(path);
for (uint32_t i = 0; i < levels && p.has_parent_path(); ++i)
p = p.parent_path();
return p.string();
}

char* buffer = (char*)malloc(length + 1);
if (!buffer) {
perror("malloc");
fclose(file);
return NULL;
}
std::string FileUtils::get_data_file(const std::string& name) {
try {
std::filesystem::path parent_dir = get_parent_directory(std::filesystem::path(__FILE__), 4);
std::filesystem::path file_path = parent_dir / "_data" / name;

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::ifstream file(file_path, std::ios::binary | std::ios::ate);
if (!file)
throw std::runtime_error("Failed to open file: " + file_path.string());

buffer[length] = 0;
fclose(file);
std::ifstream::pos_type file_size = file.tellg();
file.seekg(0, std::ios::beg);

std::string ret(buffer);
free(buffer);
return ret;
std::vector<char> buffer(static_cast<size_t>(file_size));
if (!file.read(buffer.data(), file_size))
throw std::runtime_error("Failed to read file: " + file_path.string());

return std::string(buffer.begin(), buffer.end());
} catch (const std::exception& e) {
std::cerr << e.what() << std::endl;
return {};
}
}
2 changes: 1 addition & 1 deletion cplusplus/src/p0022.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@

uint64_t p0022() {
uint64_t answer = 0;
std::string fstring = get_data_file("p0022_names.txt");
std::string fstring = FileUtils::get_data_file("p0022_names.txt");
const uint32_t name_count = 5163;
std::vector<std::string> names(5163, "");
size_t idx = 0, i = 0, pi = 0;
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:
8 changes: 4 additions & 4 deletions docs/src/cplusplus/lib/utils.rst
Original file line number Diff line number Diff line change
Expand Up @@ -3,17 +3,17 @@ utils.hpp

View source code :source:`cplusplus/src/include/utils.hpp`

.. cpp:namespace-push:: utils
.. cpp:namespace-push:: FileUtils
.. 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::filesystem::path get_parent_directory(const std::filesystem::path& 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 a34c236

Please sign in to comment.