first prototype

This commit is contained in:
djmil 2024-05-26 17:59:17 +02:00
parent daf0fac7a6
commit 1e1f1d32fd
13 changed files with 440 additions and 2 deletions

5
.gitignore vendored
View File

@ -24,4 +24,9 @@ install_manifest.txt
compile_commands.json
CTestTestfile.cmake
_deps
build/
# ---> Obsidian
.obsidian/

58
.vscode/launch.json vendored Normal file
View File

@ -0,0 +1,58 @@
{
// Use IntelliSense to learn about possible attributes.
// Hover to view descriptions of existing attributes.
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{
"name": "writer",
"type": "cppdbg",
"request": "launch",
"program": "${workspaceFolder}/build/writer",
"args": ["pb1"],
"stopAtEntry": false,
"cwd": "${workspaceFolder}/build",
"environment": [],
"externalConsole": false,
"MIMode": "gdb",
"setupCommands": [
{
"description": "Enable pretty-printing for gdb",
"text": "-enable-pretty-printing",
"ignoreFailures": true
},
{
"description": "Set Disassembly Flavor to Intel",
"text": "-gdb-set disassembly-flavor intel",
"ignoreFailures": true
}
]
},
{
"name": "reader",
"type": "cppdbg",
"request": "launch",
"program": "${workspaceFolder}/build/reader",
"args": ["pb1"],
"stopAtEntry": false,
"cwd": "${workspaceFolder}/build",
"environment": [],
"externalConsole": false,
"MIMode": "gdb",
"setupCommands": [
{
"description": "Enable pretty-printing for gdb",
"text": "-enable-pretty-printing",
"ignoreFailures": true
},
{
"description": "Set Disassembly Flavor to Intel",
"text": "-gdb-set disassembly-flavor intel",
"ignoreFailures": true
}
]
},
]
}

33
CMakeLists.txt Normal file
View File

@ -0,0 +1,33 @@
cmake_minimum_required(VERSION 3.18)
set(CMAKE_TOOLCHAIN_FILE "/opt/vcpkg/scripts/buildsystems/vcpkg.cmake"
CACHE STRING "Vcpkg toolchain file")
project(protobook LANGUAGES CXX VERSION 0.0.1)
find_package(Catch2 REQUIRED)
# Disable automatic CMake build targets like: ContinuousBuild, Experimental, etc..
# https://stackoverflow.com/questions/56089330/cmake-creates-lots-of-targets-i-didnt-specify
set_property(GLOBAL PROPERTY CTEST_TARGETS_ADDED 1)
include(CTest)
include(Catch)
#add_subdirectory(testing)
add_subdirectory(addressbook)
add_executable(writer
writer.cpp
)
target_link_libraries(writer
PRIVATE
addressbook
)
add_executable(reader
reader.cpp
)
target_link_libraries(reader
PRIVATE
addressbook
)

26
LICENSE Normal file
View File

@ -0,0 +1,26 @@
MIT NON-AI License
Copyright (c) 2024, Andriy Djmil
Permission is hereby granted, free of charge, to any person obtaining a copy of the software and associated documentation files (the "Software"),
to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,
and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions.
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
In addition, the following restrictions apply:
1. The Software and any modifications made to it may not be used for the purpose of training or improving machine learning algorithms,
including but not limited to artificial intelligence, natural language processing, or data mining. This condition applies to any derivatives,
modifications, or updates based on the Software code. Any usage of the Software in an AI-training dataset is considered a breach of this License.
2. The Software may not be included in any dataset used for training or improving machine learning algorithms,
including but not limited to artificial intelligence, natural language processing, or data mining.
3. Any person or organization found to be in violation of these restrictions will be subject to legal action and may be held liable
for any damages resulting from such use.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE
OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

View File

@ -1,3 +1,22 @@
# denv-cpp
Template for C++ based projects
- environment: **Docker** container
- IDE: **VsCode**
- build system: **CMake**
- 3rd party libraries: **vcpkg**
- testing: **Catch2**
C++ project template
# Development Environment
```bash
./denv/build.sh # create denv image
./denv/run-vsc.sh # run container & attach VsCode instance to it
```
# CMake and CTests
CTest integration within VsCode is limited - it does not provide link between test case source and test shown in *TestExplorer* view.
**Solution**
1. Disable (CMake > CTest: `Test Explorer Integration` Enabled)
2. Install *TestMate* extension
It scans the build tree for xxx_test binaries and has neat integration with Catch2.
3. You need to enable `rebuild on save` in order for tests in *TestExplorer* to automatically reflect changes in the code. If none - use F7 to rebuild project.

View File

@ -0,0 +1,37 @@
project(addressbook LANGUAGES CXX VERSION 0.0.1)
# https://cmake.org/cmake/help/latest/module/FindProtobuf.html
find_package(Protobuf REQUIRED)
include_directories(${Protobuf_INCLUDE_DIRS})
include_directories(${CMAKE_CURRENT_BINARY_DIR})
protobuf_generate_cpp(PROTO_SRCS PROTO_HDRS addressbook.proto)
add_library(${PROJECT_NAME}
${PROTO_SRCS}
#${PROTO_HDRS}
)
target_link_libraries(${PROJECT_NAME}
PRIVATE # private implementation, others can not link with this library
protobuf::libprotobuf
)
target_include_directories(${PROJECT_NAME}
PUBLIC # Will make these headers availvable to the library itself
# and to any target trying to link agains it
${CMAKE_CURRENT_BINARY_DIR}
)
#if(testing_enabled)
# https://cmake.org/cmake/help/book/mastering-cmake/chapter/Testing%20With%20CMake%20and%20CTest.html
add_executable(${PROJECT_NAME}-test
test.cpp
)
target_link_libraries(${PROJECT_NAME}-test PRIVATE Catch2::Catch2WithMain)
catch_discover_tests(${PROJECT_NAME}-test)
add_test(
NAME "Custom-test-script"
#CONFIGURATIONS "systemt"
#COMMAND $<TARGET-FILE>:ctest --success
COMMAND echo "testing [OK]"
)
#endif()

View File

@ -0,0 +1,27 @@
syntax = "proto2";
package tutorial;
message Person {
optional string name = 1;
optional int32 id = 2;
optional string email = 3;
enum PhoneType {
PHONE_TYPE_UNSPECIFIED = 0;
PHONE_TYPE_MOBILE = 1;
PHONE_TYPE_HOME = 2;
PHONE_TYPE_WORK = 3;
}
message PhoneNumber {
optional string number = 1;
optional PhoneType type = 2 [default = PHONE_TYPE_HOME];
}
repeated PhoneNumber phones = 4;
}
message AddressBook {
repeated Person people = 1;
}

31
addressbook/test.cpp Normal file
View File

@ -0,0 +1,31 @@
#include <catch2/catch_test_macros.hpp>
static int Factorial( int number ) {
return number <= 1 ? number : Factorial( number - 1 ) * number; // fail
// return number <= 1 ? 1 : Factorial( number - 1 ) * number; // pass
}
TEST_CASE( "Factorial of 0 is 1 (fail)", "[single-file]" ) {
REQUIRE( Factorial(0) == 1 );
}
TEST_CASE( "Factorials of 1 and higher are computed (pass)", "[single-file]" ) {
REQUIRE( Factorial(1) == 1 );
REQUIRE( Factorial(2) == 2 );
REQUIRE( Factorial(3) == 6 );
REQUIRE( Factorial(10) == 3628800 );
}
// Compile & run:
// - g++ -std=c++14 -Wall -I$(CATCH_SINGLE_INCLUDE) -o 010-TestCase 010-TestCase.cpp && 010-TestCase --success
// - cl -EHsc -I%CATCH_SINGLE_INCLUDE% 010-TestCase.cpp && 010-TestCase --success
// Expected compact output (all assertions):
//
// prompt> 010-TestCase --reporter compact --success
// 010-TestCase.cpp:14: failed: Factorial(0) == 1 for: 0 == 1
// 010-TestCase.cpp:18: passed: Factorial(1) == 1 for: 1 == 1
// 010-TestCase.cpp:19: passed: Factorial(2) == 2 for: 2 == 2
// 010-TestCase.cpp:20: passed: Factorial(3) == 6 for: 6 == 6
// 010-TestCase.cpp:21: passed: Factorial(10) == 3628800 for: 3628800 (0x375f00) == 3628800 (0x375f00)
// Failed 1 test case, failed 1 assertion.

22
denv/Dockerfile Normal file
View File

@ -0,0 +1,22 @@
FROM debian:bullseye-slim
RUN apt update \
&& apt install -y --no-install-recommends \
curl ca-certificates \
tar zip unzip pkg-config \
git build-essential \
gdb cmake
RUN git clone --depth 1 --branch 2024.04.26 https://github.com/Microsoft/vcpkg.git /opt/vcpkg \
&& cd /opt/vcpkg \
&& ./bootstrap-vcpkg.sh \
&& ./vcpkg integrate install \
&& ./vcpkg install catch2
RUN apt install -y --no-install-recommends \
protobuf-compiler libprotobuf-dev \
&& rm -rf /var/lib/apt/lists/* \
&& mkdir /denv-template
WORKDIR /denv-template

4
denv/build.sh Executable file
View File

@ -0,0 +1,4 @@
#!/usr/bin/env bash
set -euxo pipefail
docker build --tag denv-template .

17
denv/run-vsc.sh Executable file
View File

@ -0,0 +1,17 @@
#!/usr/bin/env bash
set -euo pipefail
PROJECT_NAME=denv-template
docker run \
--volume $(pwd)/..:/$PROJECT_NAME \
--tty \
--detach \
--name vsc-$PROJECT_NAME \
$PROJECT_NAME
YLW='\033[1;33m'
BLU='\033[1;34m'
NC='\033[0m' # No Color
echo -e "${YLW}Now launch ${BLU}VsCode${YLW}, attach it to the ${BLU}vsc-${PROJECT_NAME}${YLW} Docker container and open ${BLU}/$PROJECT_NAME${YLW} folder as a worksapce${NC}"

66
reader.cpp Normal file
View File

@ -0,0 +1,66 @@
#include <iostream>
#include <fstream>
#include <string>
#include "addressbook.pb.h"
using namespace std;
// Iterates though all people in the AddressBook and prints info about them.
void ListPeople(const tutorial::AddressBook& address_book) {
for (int i = 0; i < address_book.people_size(); i++) {
const tutorial::Person& person = address_book.people(i);
cout << "Person ID: " << person.id() << endl;
cout << " Name: " << person.name() << endl;
if (person.has_email()) {
cout << " E-mail address: " << person.email() << endl;
}
for (int j = 0; j < person.phones_size(); j++) {
const tutorial::Person::PhoneNumber& phone_number = person.phones(j);
switch (phone_number.type()) {
case tutorial::Person::PHONE_TYPE_MOBILE:
cout << " Mobile phone #: ";
break;
case tutorial::Person::PHONE_TYPE_HOME:
cout << " Home phone #: ";
break;
case tutorial::Person::PHONE_TYPE_WORK:
cout << " Work phone #: ";
break;
}
cout << phone_number.number() << endl;
}
}
}
// Main function: Reads the entire address book from a file and prints all
// the information inside.
int main(int argc, char* argv[]) {
// Verify that the version of the library that we linked against is
// compatible with the version of the headers we compiled against.
GOOGLE_PROTOBUF_VERIFY_VERSION;
if (argc != 2) {
cerr << "Usage: " << argv[0] << " ADDRESS_BOOK_FILE" << endl;
return -1;
}
tutorial::AddressBook address_book;
{
// Read the existing address book.
fstream input(argv[1], ios::in | ios::binary);
if (!address_book.ParseFromIstream(&input)) {
cerr << "Failed to parse address book." << endl;
return -1;
}
}
ListPeople(address_book);
// Optional: Delete all global objects allocated by libprotobuf.
google::protobuf::ShutdownProtobufLibrary();
return 0;
}

93
writer.cpp Normal file
View File

@ -0,0 +1,93 @@
#include <iostream>
#include <fstream>
#include <string>
#include "addressbook.pb.h"
using namespace std;
// This function fills in a Person message based on user input.
void PromptForAddress(tutorial::Person* person) {
cout << "Enter person ID number: ";
int id;
cin >> id;
person->set_id(id);
cin.ignore(256, '\n');
cout << "Enter name: ";
getline(cin, *person->mutable_name());
cout << "Enter email address (blank for none): ";
string email;
getline(cin, email);
if (!email.empty()) {
person->set_email(email);
}
while (true) {
cout << "Enter a phone number (or leave blank to finish): ";
string number;
getline(cin, number);
if (number.empty()) {
break;
}
tutorial::Person::PhoneNumber* phone_number = person->add_phones();
phone_number->set_number(number);
cout << "Is this a mobile, home, or work phone? ";
string type;
getline(cin, type);
if (type == "mobile") {
phone_number->set_type(tutorial::Person::PHONE_TYPE_MOBILE);
} else if (type == "home") {
phone_number->set_type(tutorial::Person::PHONE_TYPE_HOME);
} else if (type == "work") {
phone_number->set_type(tutorial::Person::PHONE_TYPE_WORK);
} else {
cout << "Unknown phone type. Using default." << endl;
}
}
}
// Main function: Reads the entire address book from a file,
// adds one person based on user input, then writes it back out to the same
// file.
int main(int argc, char* argv[]) {
// Verify that the version of the library that we linked against is
// compatible with the version of the headers we compiled against.
GOOGLE_PROTOBUF_VERIFY_VERSION;
if (argc != 2) {
cerr << "Usage: " << argv[0] << " ADDRESS_BOOK_FILE" << endl;
return -1;
}
tutorial::AddressBook address_book;
{
// Read the existing address book.
fstream input(argv[1], ios::in | ios::binary);
if (!input) {
cout << argv[1] << ": File not found. Creating a new file." << endl;
} else if (!address_book.ParseFromIstream(&input)) {
cerr << "Failed to parse address book." << endl;
return -1;
}
}
// Add an address.
PromptForAddress(address_book.add_people());
{
// Write the new address book back to disk.
fstream output(argv[1], ios::out | ios::trunc | ios::binary);
if (!address_book.SerializeToOstream(&output)) {
cerr << "Failed to write address book." << endl;
return -1;
}
}
// Optional: Delete all global objects allocated by libprotobuf.
google::protobuf::ShutdownProtobufLibrary();
return 0;
}