Skip to content

Commit 8acca4c

Browse files
authored
Merge pull request #18 from sergeyklay/chucknorris-pimpl
Hide implementation details of a public Chuck Norris' header
2 parents 7b4f9bd + 9579d54 commit 8acca4c

File tree

8 files changed

+175
-84
lines changed

8 files changed

+175
-84
lines changed

cmake/CompilerWarnings.cmake

Lines changed: 15 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -17,31 +17,30 @@ include(CMakeDependentOption)
1717
option(WARNINGS_AS_ERRORS "Turn all build warnings into errors")
1818

1919
add_library(compilerwarnings INTERFACE)
20-
add_library(Stars::CompilerWarnings ALIAS compilerwarnings)
20+
add_library(stars::CompilerWarnings ALIAS compilerwarnings)
2121

22-
# Clang / GCC:
23-
# -Wall Baseline reasonable warnings
24-
# -Wextra Reasonable and standard
25-
# -Wpedantic Warn if non-standard C++ is used
26-
# -Wshadow Warn if a variable declaration shadows one from a parent context
27-
# -Wsign-conversion Warn for implicit conversions that may change the sign of an integer value
28-
# -Wswitch-enum Warn whenever a "switch" lacks a "case"
29-
set(unix-warnings -Wall -Wextra -Wpedantic -Wshadow -Wsign-conversion
30-
-Wswitch-enum)
22+
# Clang / GCC
23+
# For "-Werror" see target_compile_options() bellow
24+
set(unix-warnings
25+
-Wall # Baseline reasonable warnings
26+
-Wextra # Reasonable and standard
27+
-Wpedantic # Warn if non-standard C++ is used
28+
-Wshadow # Warn if a variable declaration shadows one from a parent context
29+
-Wsign-conversion # Warn for implicit conversions that may change the sign of an integer value
30+
-Wswitch-enum) # Warn whenever a "switch" lacks a "case"
3131

32-
# MSVC:
33-
# /WX Baseline reasonable warnings
34-
set(msvc-warnings /W4)
32+
# MSVC
33+
# For "/WX" see target_compile_options() bellow
34+
set(msvc-warnings
35+
/W4) # Baseline reasonable warnings
3536

3637
# This is recognized as a valid compiler flag only by GCC
3738
if(CMAKE_COMPILER_IS_GNUCXX)
3839
# Warn for constructs that violate guidelines in Effective C++
3940
list(APPEND unix-warnings -Weffc++)
4041
endif()
4142

42-
# Treat compiler warnings as errors
43-
# MSVC: /W4
44-
# Clang / GCC: -Werror
43+
# Enable all flags
4544
target_compile_options(
4645
compilerwarnings
4746
INTERFACE $<$<CXX_COMPILER_ID:MSVC>:${msvc-warnings}

include/stars/ChuckNorris.hpp

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -18,17 +18,17 @@
1818
#ifndef STARS_CHUCKNORRIS_HPP_
1919
#define STARS_CHUCKNORRIS_HPP_
2020

21-
#include <sqlite3.h>
22-
2321
#include <string>
2422

2523
namespace stars {
2624

27-
/// \brief Chuck Norris class.
25+
/// \brief Chuck Norris Implementation class.
26+
class ChuckNorrisImpl;
27+
28+
/// \brief Public Chuck Norris class.
2829
class ChuckNorris {
2930
public:
3031
ChuckNorris();
31-
~ChuckNorris();
3232

3333
// Make sure you cannot copy Chuck Norris
3434
ChuckNorris(ChuckNorris const &) = delete;
@@ -42,8 +42,7 @@ class ChuckNorris {
4242
[[nodiscard]] int getStatus() const;
4343

4444
private:
45-
sqlite3 *db = nullptr;
46-
int dbStatus = 0;
45+
stars::ChuckNorrisImpl *pimpl;
4746
};
4847
} // namespace stars
4948

src/CMakeLists.txt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,8 +25,8 @@ target_include_directories(stars
2525
PRIVATE $<BUILD_INTERFACE:${PROJECT_SOURCE_DIR}/src>)
2626

2727
target_link_libraries(stars
28-
PRIVATE Stars::CompilerWarnings
29-
Stars::ChuckNorris)
28+
PRIVATE stars::CompilerWarnings
29+
stars::ChuckNorris)
3030

3131
file(RELATIVE_PATH _rel
3232
${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_BINDIR}

src/ChuckNorris/CMakeLists.txt

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -31,19 +31,21 @@ get_filename_component(_MOD_DIR ${input} DIRECTORY)
3131
configure_file("${_MOD_DIR}/Database.hpp.in" "${_MOD_DIR}/Database.hpp")
3232

3333
# Add definition for chucknorris library
34-
add_library(chucknorris SHARED ChuckNorris.cpp)
35-
add_library(Stars::ChuckNorris ALIAS chucknorris)
34+
add_library(chucknorris SHARED
35+
ChuckNorris.cpp
36+
ChuckNorrisImpl.cpp)
37+
add_library(stars::ChuckNorris ALIAS chucknorris)
3638

3739
target_link_libraries(chucknorris
38-
PUBLIC CONAN_PKG::sqlite3
39-
CONAN_PKG::spdlog
40-
PRIVATE Stars::CompilerWarnings)
40+
PUBLIC CONAN_PKG::spdlog
41+
PRIVATE CONAN_PKG::sqlite3
42+
stars::CompilerWarnings)
4143

4244
target_include_directories(chucknorris
4345
PUBLIC $<BUILD_INTERFACE:${PROJECT_SOURCE_DIR}/include>
4446
$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>)
4547

46-
# Version info
48+
# Library version info
4749
set(CHUCKNORRIS_VERSION_MAJOR 1)
4850
set(CHUCKNORRIS_VERSION_MINOR 13)
4951
set(CHUCKNORRIS_VERSION_PATCH 42)

src/ChuckNorris/ChuckNorris.cpp

Lines changed: 23 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -15,66 +15,37 @@
1515
#include "stars/ChuckNorris.hpp"
1616

1717
#include <spdlog/spdlog.h>
18-
#include <sqlite3.h>
1918

20-
#include "Database.hpp"
19+
#include "ChuckNorrisImpl.hpp"
2120

22-
stars::ChuckNorris::ChuckNorris() {
23-
int flags = SQLITE_OPEN_URI | SQLITE_OPEN_READONLY;
24-
if (sqlite3_open_v2(STARS_DB_URI, &db, flags, nullptr) != SQLITE_OK) {
25-
spdlog::critical("Can't open database: {}", sqlite3_errmsg(db));
26-
dbStatus = -1;
27-
sqlite3_close(db);
28-
} else {
29-
dbStatus = 1;
21+
stars::ChuckNorris::ChuckNorris() : pimpl(new stars::ChuckNorrisImpl) {
22+
if (getStatus() == -1) {
23+
spdlog::error("Can't open database: {}", pimpl->getDbMessage());
3024
}
3125
}
3226

33-
stars::ChuckNorris::~ChuckNorris() {
34-
if (dbStatus == 1) {
35-
spdlog::debug("Closing a database connection...");
36-
sqlite3_close(db);
37-
}
38-
}
39-
int stars::ChuckNorris::getStatus() const { return dbStatus; }
27+
int stars::ChuckNorris::getStatus() const { return pimpl->getDbStatus(); }
4028

4129
std::string stars::ChuckNorris::getFact() {
42-
if (dbStatus != 1) {
43-
return "";
44-
}
45-
46-
sqlite3_stmt* stmt;
47-
int rc;
48-
std::string res;
49-
auto const q = R"(SELECT fact FROM chucknorris ORDER BY RANDOM() LIMIT 1;)";
50-
51-
rc = sqlite3_prepare_v2(db, q, -1, &stmt, nullptr);
52-
if (rc != SQLITE_OK) {
53-
spdlog::error("Failed to prepare database query: {}", sqlite3_errmsg(db));
54-
dbStatus = -2;
55-
56-
sqlite3_finalize(stmt);
57-
return "";
58-
}
59-
60-
rc = sqlite3_step(stmt);
61-
if (rc != SQLITE_ROW) {
62-
if (rc != SQLITE_DONE) {
63-
spdlog::error("Failed to select fact: {}", sqlite3_errmsg(db));
64-
dbStatus = -2;
65-
} else {
66-
spdlog::info("Unable to select fact: database is empty");
67-
dbStatus = -1;
30+
auto fact = pimpl->getFact();
31+
auto stat = getStatus();
32+
33+
if (stat < 0) {
34+
auto message = pimpl->getDbMessage();
35+
switch (getStatus()) {
36+
case -2:
37+
spdlog::error("Failed to prepare database query: {}", message);
38+
break;
39+
case -3:
40+
spdlog::error("Failed to select fact: {}", message);
41+
break;
42+
case -4:
43+
spdlog::error("Unable to select fact: database is empty");
44+
break;
45+
default:
46+
spdlog::error("Something went wrong");
6847
}
69-
70-
sqlite3_finalize(stmt);
71-
return "";
7248
}
7349

74-
auto sqlite_row = sqlite3_column_text(stmt, 0);
75-
auto row = reinterpret_cast<const char*>(sqlite_row);
76-
res = std::string(row);
77-
78-
sqlite3_finalize(stmt);
79-
return res;
50+
return fact;
8051
}
Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
// Copyright 2020 Serghei Iakovlev <egrep@protonmail.ch>
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
#include "ChuckNorrisImpl.hpp"
16+
17+
#include "Database.hpp"
18+
19+
stars::ChuckNorrisImpl::ChuckNorrisImpl() : dbMessage() {
20+
int flags = SQLITE_OPEN_URI | SQLITE_OPEN_READONLY;
21+
if (sqlite3_open_v2(STARS_DB_URI, &db, flags, nullptr) != SQLITE_OK) {
22+
dbStatus = -1;
23+
dbMessage = std::string(sqlite3_errmsg(db));
24+
sqlite3_close(db);
25+
} else {
26+
dbStatus = 1;
27+
}
28+
}
29+
30+
stars::ChuckNorrisImpl::~ChuckNorrisImpl() {
31+
if (dbStatus == 1) {
32+
sqlite3_close(db);
33+
}
34+
}
35+
36+
int stars::ChuckNorrisImpl::getDbStatus() const { return dbStatus; }
37+
38+
std::string stars::ChuckNorrisImpl::getDbMessage() const { return dbMessage; }
39+
40+
std::string stars::ChuckNorrisImpl::getFact() {
41+
std::string res;
42+
sqlite3_stmt* stmt;
43+
int rc;
44+
45+
if (dbStatus != 1) {
46+
return res;
47+
}
48+
49+
auto const q = R"(SELECT fact FROM chucknorris ORDER BY RANDOM() LIMIT 1;)";
50+
rc = sqlite3_prepare_v2(db, q, -1, &stmt, nullptr);
51+
52+
if (rc != SQLITE_OK) {
53+
dbMessage = std::string(sqlite3_errmsg(db));
54+
dbStatus = -2;
55+
} else {
56+
rc = sqlite3_step(stmt);
57+
if (rc != SQLITE_ROW) {
58+
dbMessage = std::string(sqlite3_errmsg(db));
59+
dbStatus = -4;
60+
if (rc != SQLITE_DONE) {
61+
dbStatus = -3;
62+
}
63+
} else {
64+
auto sqlite_row = sqlite3_column_text(stmt, 0);
65+
auto row = reinterpret_cast<const char*>(sqlite_row);
66+
res = std::string(row);
67+
}
68+
}
69+
70+
sqlite3_finalize(stmt);
71+
return res;
72+
}
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
// Copyright 2020 Serghei Iakovlev <egrep@protonmail.ch>
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
#ifndef STARS_CHUCKNORRIS_CHUCKNORRISIMPL_HPP_
16+
#define STARS_CHUCKNORRIS_CHUCKNORRISIMPL_HPP_
17+
18+
#include <sqlite3.h>
19+
20+
#include <string>
21+
22+
namespace stars {
23+
class ChuckNorrisImpl {
24+
public:
25+
ChuckNorrisImpl();
26+
~ChuckNorrisImpl();
27+
28+
// Make sure you cannot copy Chuck Norris Implementation
29+
ChuckNorrisImpl(ChuckNorrisImpl const &) = delete;
30+
ChuckNorrisImpl &operator=(ChuckNorrisImpl const &) = delete;
31+
32+
// Make sure you cannot move Chuck Norris Implementation
33+
ChuckNorrisImpl(ChuckNorrisImpl &&) = delete;
34+
ChuckNorrisImpl &operator=(ChuckNorrisImpl &&) = delete;
35+
36+
std::string getFact();
37+
38+
[[nodiscard]] int getDbStatus() const;
39+
[[nodiscard]] std::string getDbMessage() const;
40+
41+
private:
42+
sqlite3 *db = nullptr;
43+
std::string dbMessage;
44+
int dbStatus = 0;
45+
};
46+
} // namespace stars
47+
48+
#endif // STARS_CHUCKNORRIS_CHUCKNORRISIMPL_HPP_

tests/ChuckNorris/CMakeLists.txt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,8 +27,8 @@ target_include_directories(ChuckNorrisTest
2727
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}>)
2828

2929
target_link_libraries(ChuckNorrisTest
30-
PRIVATE Stars::CompilerWarnings
31-
Stars::ChuckNorris
30+
PRIVATE stars::CompilerWarnings
31+
stars::ChuckNorris
3232
CONAN_PKG::gtest)
3333

3434
gtest_discover_tests(ChuckNorrisTest

0 commit comments

Comments
 (0)