diff --git a/include/cppIni/cppIni_c.h b/include/cppIni/cppIni_c.h new file mode 100644 index 0000000..dbe9a82 --- /dev/null +++ b/include/cppIni/cppIni_c.h @@ -0,0 +1,44 @@ +// cppIni - A C++20 library for reading and writing INI files +// Copyright (C) 2023-2024 Nils Hofmann +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +#pragma once + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +typedef void* pFile; +/// \file cppIni_c.h +/// \brief The C API for cppIni +/// +/// \details +/// This file contains the C API for cppIni. It is a very thin wrapper around +/// the C++ API. It is intended for use with languages that do not support +/// C++. + +CPPINI_EXPORT pFile cppIni_open(const char* filename); ///< Opens a file +CPPINI_EXPORT void cppIni_close(pFile* file); ///< Closes a file +CPPINI_EXPORT void cppIni_set(pFile file, const char* section, const char* key, const char* value); ///< Sets a value +CPPINI_EXPORT const char* cppIni_gets(pFile file, const char* section, const char* key, char* out, size_t outSize); ///< Gets a string +CPPINI_EXPORT int cppIni_geti(pFile file, const char* section, const char* key); ///< Gets an integer +CPPINI_EXPORT float cppIni_getf(pFile file, const char* section, const char* key); ///< Gets a float + +#ifdef __cplusplus +} +#endif diff --git a/src/CInterface.cpp b/src/CInterface.cpp new file mode 100644 index 0000000..8d6db42 --- /dev/null +++ b/src/CInterface.cpp @@ -0,0 +1,84 @@ +// cppIni - A C++20 library for reading and writing INI files +// Copyright (C) 2023-2024 Nils Hofmann +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +#include + +#include +#include + +/// Opens a file for reading and writing. Throws an exception if the file cannot +/// be opened. +/// +/// \param[in] filename The name of the file to open +/// \return A pointer to a File object +pFile cppIni_open(const char* filename) +{ + return new File(filename); +} + +/// Closes a file that was opened with cppIni_open(). +/// \param[in] file A pointer to a File object +void cppIni_close(pFile* file) +{ + delete static_cast(*file); +} + +/// \param[in] file A pointer to a File object +/// \param[in] section The name of the section to add +/// \param[in] key The name of the key to add +/// \param[in] value The value to add +void cppIni_set(pFile file, const char* section, const char* key, const char* value) +{ + static_cast(file)->set(section, key, value); +} + +/// \param[in] file A pointer to a File object +/// \param[in] section The name of the section to get +/// \param[in] key The name of the key to get +/// \param[out] out A buffer to store the value in +/// \param[in] outSize The size of the buffer +/// \return A pointer to the buffer +const char* cppIni_gets(pFile file, const char* section, const char* key, char* out, size_t outSize) +{ + const auto value = static_cast(file)->get(section, key); + + if (value.empty()) { + return out; + } + + std::strncpy(out, value.data(), outSize); + return out; +} + +/// \see cppIni_gets +/// \param[in] file A pointer to a File object +/// \param[in] section The name of the section to get +/// \param[in] key The name of the key to get +/// \return The value of the key +int cppIni_geti(pFile file, const char* section, const char* key) +{ + return static_cast(file)->get(section, key); +} + +/// \see cppIni_gets +/// \param[in] file A pointer to a File object +/// \param[in] section The name of the section to get +/// \param[in] key The name of the key to get +/// \return The value of the key +float cppIni_getf(pFile file, const char* section, const char* key) +{ + return static_cast(file)->get(section, key); +} diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index a6c813e..eecc4e7 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -3,6 +3,7 @@ cmake_minimum_required(VERSION 3.24) set(CMAKE_DEBUG_POSTFIX d) set(SOURCES + CInterface.cpp Entry.cpp File.cpp Section.cpp @@ -10,6 +11,7 @@ set(SOURCES set(API_HEADERS cppIni.h + cppIni_c.h Entry.h File.h Section.h diff --git a/tests/CInterfaceTest.cpp b/tests/CInterfaceTest.cpp new file mode 100644 index 0000000..a979e59 --- /dev/null +++ b/tests/CInterfaceTest.cpp @@ -0,0 +1,99 @@ +// cppIni - A C++20 library for reading and writing INI files +// Copyright (C) 2023-2024 Nils Hofmann +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +#include +#include +#include + +#include +#include + +static const std::string fileName = std::format("{}{}", WORKING_DIR, "/res/test.ini");; + +TEST_SUITE_BEGIN("CInterface"); + +TEST_CASE("Construction of File object") +{ + void* file; + CHECK_NOTHROW(file = cppIni_open(fileName.c_str())); + CHECK_NE(file, nullptr); + CHECK_NOTHROW(cppIni_close(&file)); +} + +struct ScopeGuard +{ + ScopeGuard(pFile file) : file(file){}; + ~ScopeGuard(){ cppIni_close(&file); }; + pFile file; +}; + +TEST_CASE("Read a string entry") +{ + void* file = cppIni_open(fileName.c_str()); + ScopeGuard guard{file}; + + std::array buffer{0}; + cppIni_gets(file, "Section1", "Entry1", buffer.data(), buffer.size()); + + CHECK_EQ(std::string_view{buffer.data()}, "Value1"); +} + +TEST_CASE("Try to read a non-existing entry") +{ + void* file = cppIni_open(fileName.c_str()); + ScopeGuard guard{file}; + + std::array buffer{0}; + CHECK_EQ(cppIni_gets(file, "Section1", "NonExistingEntry", buffer.data(), buffer.size()), buffer.data()); + CHECK_EQ(buffer[0], '\0'); +} + +TEST_CASE("Change a value") +{ + constexpr auto tempFileName = "tmp.ini"; + std::filesystem::copy_file(fileName, tempFileName); + + { + constexpr auto newValue = 1337; + void* file = cppIni_open(tempFileName); + ScopeGuard guard{file}; + + const auto previousValue = cppIni_geti(file, "Section1", "IntEntry"); + CHECK_NE(previousValue, newValue); + cppIni_set(file, "Section1", "IntEntry", std::to_string(newValue).c_str()); + CHECK_EQ(cppIni_geti(file, "Section1", "IntEntry"), newValue); + } + + std::filesystem::remove(tempFileName); +} + +TEST_CASE("Read an integer entry") +{ + void* file = cppIni_open(fileName.c_str()); + ScopeGuard guard{file}; + + CHECK_EQ(cppIni_geti(file, "Section1", "IntEntry"), 42); +} + +TEST_CASE("Read a floating point value entry") +{ + void* file = cppIni_open(fileName.c_str()); + ScopeGuard guard{file}; + + CHECK_LT(std::abs(cppIni_getf(file, "Section1.Subsection1", "DoubleEntry") - 3.1415), 0.001); +} + +TEST_SUITE_END(); diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 4c78aa1..f086d2d 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -6,6 +6,7 @@ set(TEST_SOURCES EntryTest.cpp FileTest.cpp SectionTest.cpp + CInterfaceTest.cpp ) add_executable(${PROJECT_NAME}_tests ${TEST_SOURCES})