Skip to content

Commit 6ec6289

Browse files
committed
Use Abseil in case exceptions are disabled
1 parent 4aa1934 commit 6ec6289

File tree

5 files changed

+115
-130
lines changed

5 files changed

+115
-130
lines changed

CMakeLists.txt

Lines changed: 65 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,36 @@ project(hnswlib
55

66
include(GNUInstallDirs)
77
include(CheckCXXCompilerFlag)
8+
include(FetchContent)
9+
10+
function(add_example_or_test TARGET_NAME ...)
11+
add_executable(${ARGV})
12+
target_link_libraries("${TARGET_NAME}" ${common_link_libraries})
13+
target_include_directories("${TARGET_NAME}" PUBLIC "${abseil-cpp_SOURCE_DIR}")
14+
if(HNSWLIB_USE_ABSEIL)
15+
target_compile_definitions("${TARGET_NAME}" PUBLIC HNSWLIB_USE_ABSEIL)
16+
endif()
17+
endfunction()
18+
19+
option(HNSWLIB_USE_ABSEIL "Whether to use Abseil in hnswlib" OFF)
20+
21+
if(HNSWLIB_USE_ABSEIL)
22+
FetchContent_Declare(
23+
abseil-cpp
24+
GIT_REPOSITORY https://github.com/abseil/abseil-cpp
25+
GIT_TAG 20250127.1
26+
)
27+
28+
FetchContent_MakeAvailable(abseil-cpp)
29+
30+
if("${abseil-cpp_SOURCE_DIR}" STREQUAL "")
31+
message(FATAL "Failed to find Abseil source directory")
32+
endif()
33+
# Abseil requires C++14 or later.
34+
set(CMAKE_CXX_STANDARD 14)
35+
else()
36+
set(CMAKE_CXX_STANDARD 11)
37+
endif()
838

939
add_library(hnswlib INTERFACE)
1040
add_library(hnswlib::hnswlib ALIAS hnswlib)
@@ -32,10 +62,8 @@ else()
3262
option(HNSWLIB_EXAMPLES "Build examples and tests." OFF)
3363
endif()
3464
if(HNSWLIB_EXAMPLES)
35-
set(CMAKE_CXX_STANDARD 11)
36-
3765
if (CMAKE_CXX_COMPILER_ID MATCHES "Clang")
38-
SET( CMAKE_CXX_FLAGS "-Ofast -std=c++11 -DHAVE_CXX0X -openmp -fpic -ftree-vectorize" )
66+
SET( CMAKE_CXX_FLAGS "-Ofast -DHAVE_CXX0X -openmp -fpic -ftree-vectorize" )
3967
check_cxx_compiler_flag("-march=native" COMPILER_SUPPORT_NATIVE_FLAG)
4068
if(COMPILER_SUPPORT_NATIVE_FLAG)
4169
SET( CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -march=native" )
@@ -48,58 +76,43 @@ if(HNSWLIB_EXAMPLES)
4876
endif()
4977
endif()
5078
elseif (CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
51-
SET( CMAKE_CXX_FLAGS "-Ofast -lrt -std=c++11 -DHAVE_CXX0X -march=native -fpic -w -fopenmp -ftree-vectorize -ftree-vectorizer-verbose=0" )
79+
SET( CMAKE_CXX_FLAGS "-Ofast -lrt -DHAVE_CXX0X -march=native -fpic -w -fopenmp -ftree-vectorize -ftree-vectorizer-verbose=0" )
5280
elseif (CMAKE_CXX_COMPILER_ID STREQUAL "MSVC")
5381
SET( CMAKE_CXX_FLAGS "/O2 -DHAVE_CXX0X /W1 /openmp /EHsc" )
5482
endif()
5583

56-
# examples
57-
add_executable(example_search examples/cpp/example_search.cpp)
58-
target_link_libraries(example_search hnswlib)
59-
60-
add_executable(example_epsilon_search examples/cpp/example_epsilon_search.cpp)
61-
target_link_libraries(example_epsilon_search hnswlib)
62-
63-
add_executable(example_multivector_search examples/cpp/example_multivector_search.cpp)
64-
target_link_libraries(example_multivector_search hnswlib)
65-
66-
add_executable(example_filter examples/cpp/example_filter.cpp)
67-
target_link_libraries(example_filter hnswlib)
68-
69-
add_executable(example_replace_deleted examples/cpp/example_replace_deleted.cpp)
70-
target_link_libraries(example_replace_deleted hnswlib)
71-
72-
add_executable(example_mt_search examples/cpp/example_mt_search.cpp)
73-
target_link_libraries(example_mt_search hnswlib)
74-
75-
add_executable(example_mt_filter examples/cpp/example_mt_filter.cpp)
76-
target_link_libraries(example_mt_filter hnswlib)
77-
78-
add_executable(example_mt_replace_deleted examples/cpp/example_mt_replace_deleted.cpp)
79-
target_link_libraries(example_mt_replace_deleted hnswlib)
80-
81-
# tests
82-
add_executable(multivector_search_test tests/cpp/multivector_search_test.cpp)
83-
target_link_libraries(multivector_search_test hnswlib)
84-
85-
add_executable(epsilon_search_test tests/cpp/epsilon_search_test.cpp)
86-
target_link_libraries(epsilon_search_test hnswlib)
87-
88-
add_executable(test_updates tests/cpp/updates_test.cpp)
89-
target_link_libraries(test_updates hnswlib)
90-
91-
add_executable(searchKnnCloserFirst_test tests/cpp/searchKnnCloserFirst_test.cpp)
92-
target_link_libraries(searchKnnCloserFirst_test hnswlib)
93-
94-
add_executable(searchKnnWithFilter_test tests/cpp/searchKnnWithFilter_test.cpp)
95-
target_link_libraries(searchKnnWithFilter_test hnswlib)
96-
97-
add_executable(multiThreadLoad_test tests/cpp/multiThreadLoad_test.cpp)
98-
target_link_libraries(multiThreadLoad_test hnswlib)
99-
100-
add_executable(multiThread_replace_test tests/cpp/multiThread_replace_test.cpp)
101-
target_link_libraries(multiThread_replace_test hnswlib)
84+
set(common_link_libraries
85+
hnswlib)
86+
if(HNSWLIB_USE_ABSEIL)
87+
list(APPEND common_link_libraries absl::status absl::statusor)
88+
endif()
10289

103-
add_executable(main tests/cpp/main.cpp tests/cpp/sift_1b.cpp)
104-
target_link_libraries(main hnswlib)
90+
set(EXAMPLE_NAMES
91+
example_search
92+
example_epsilon_search
93+
example_multivector_search
94+
example_filter
95+
example_replace_deleted
96+
example_mt_search
97+
example_mt_replace_deleted)
98+
99+
foreach(example_name IN LISTS EXAMPLE_NAMES)
100+
add_example_or_test("${example_name}" "examples/cpp/${example_name}.cpp")
101+
endforeach()
102+
103+
set(TEST_NAMES
104+
multivector_search_test
105+
epsilon_search_test
106+
searchKnnCloserFirst_test
107+
searchKnnWithFilter_test
108+
multiThreadLoad_test
109+
multiThread_replace_test)
110+
foreach(test_name IN LISTS TEST_NAMES)
111+
add_example_or_test("${test_name}" "tests/cpp/${test_name}.cpp")
112+
endforeach()
113+
114+
# This test deviates from the above pattern of naming test executables.
115+
add_example_or_test(test_updates tests/cpp/updates_test.cpp)
116+
117+
add_example_or_test(main tests/cpp/main.cpp tests/cpp/sift_1b.cpp)
105118
endif()

hnswlib/bruteforce.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,7 @@ class BruteforceSearch : public AlgorithmInterface<dist_t> {
8383
}
8484
memcpy(data_ + size_per_element_ * idx + data_size_, &label, sizeof(labeltype));
8585
memcpy(data_ + size_per_element_ * idx, datapoint, data_size_);
86+
HNSWLIB_RETURN_OK_STATUS;
8687
}
8788

8889

hnswlib/hnswalg.h

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -825,12 +825,11 @@ class HierarchicalNSW : public AlgorithmInterface<dist_t> {
825825
HNSWLIB_RETURN_OK_STATUS;
826826
}
827827

828-
829828
template<typename data_t>
830-
#if HNSWLIB_USE_EXCEPTIONS
831-
std::vector<data_t>
829+
#if HNSWLIB_USE_ABSEIL
830+
::absl::StatusOr<std::vector<data_t>>
832831
#else
833-
StatusOr<std::vector<data_t>>
832+
std::vector<data_t>
834833
#endif
835834
getDataByLabel(labeltype label) const {
836835
// lock all operations with element by label

hnswlib/hnswlib.h

Lines changed: 45 additions & 74 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,5 @@
11
#pragma once
22

3-
#include <utility>
4-
53
// https://github.com/nmslib/hnswlib/pull/508
64
// This allows others to provide their own error stream (e.g. RcppHNSW)
75
#ifndef HNSWLIB_ERR_OVERRIDE
@@ -121,8 +119,15 @@ static bool AVX512Capable() {
121119
#include <queue>
122120
#include <vector>
123121
#include <iostream>
122+
#include <utility>
124123
#include <string.h>
125124

125+
#if HNSWLIB_USE_ABSEIL
126+
#include "absl/status/status.h"
127+
#include "absl/status/statusor.h"
128+
#include "absl/strings/string_view.h"
129+
#endif
130+
126131
namespace hnswlib {
127132

128133
// hnswlib can be compiled in two modes: with and without exceptions.
@@ -139,102 +144,68 @@ namespace hnswlib {
139144
// HNSWLIB_STATUS_OR_TYPE(t) -- return type for a function with a return value
140145
// HNSWLIB_RETURN_OK_STATUS -- return an OK status or simply return.
141146

142-
#define HNSWLIB_USE_EXCEPTIONS 0
143-
144-
#ifndef HNSWLIB_USE_EXCEPTIONS
145-
#define HNSWLIB_USE_EXCEPTIONS 1
146-
#endif
147-
148-
#if HNSWLIB_USE_EXCEPTIONS
147+
#if !HNSWLIB_USE_ABSEIL
149148

150149
#define HNSWLIB_THROW_ERROR(message) \
151150
throw std::runtime_error(message);
152151
#define HNSWLIB_STATUS_TYPE void
153152
#define HNSWLIB_STATUS_OR_TYPE(t) t
154153
#define HNSWLIB_RETURN_OK_STATUS return
155154
#define HNSWLIB_EXTRACT_VALUE(status_or_value) (status_or_value)
155+
#define HNSWLIB_ASSIGN_OR_RETURN(lhs, expression) lhs = (expression)
156156
#define HNSWLIB_RETURN_IF_VALUE_NOT_OK(status_or_value) status_or_value
157157
#define HNSWLIB_ASSIGN_VALUE_OR_THROW_IN_TEST(identifier, expression) \
158158
auto identifier = (expression)
159-
#else
160-
161-
class Status {
162-
public:
163-
// Default constructor (OK status)
164-
Status() : message_(nullptr) {}
165-
166-
// Constructor with an error message
167-
Status(const char* message) : message_(message) {}
168-
169-
// Returns true if the status is ok
170-
bool ok() const { return message_ == nullptr; }
171-
172-
// Returns the error message, or nullptr if ok
173-
const char* message() const { return message_; }
174-
175-
private:
176-
const char* message_; // Null if ok, error message otherwise
177-
};
178-
179-
template <typename T>
180-
class StatusOr {
181-
public:
182-
// Default constructor
183-
StatusOr() : status_(), value_() {}
184159

185-
// Constructor with a value
186-
StatusOr(T value) : status_(), value_(value) {}
187-
188-
// Constructor with an error status
189-
StatusOr(const char* error) : status_(error), value_() {}
190-
StatusOr(Status status) : status_(status), value_() {}
191-
192-
// Returns true if the status is ok
193-
bool ok() const { return status_.ok(); }
194-
195-
// Returns the value if the status is ok
196-
T&& value() {
197-
return std::move(value_);
198-
}
199-
200-
const T& value() const {
201-
return value_;
202-
}
203-
204-
// Returns the value using the dereference operator
205-
T operator*() const {
206-
return value();
207-
}
208-
209-
// Returns the error status if not ok
210-
Status status() const { return status_; }
211-
212-
private:
213-
Status status_; // Status object
214-
T value_; // The value
215-
};
160+
#else
216161

217-
#define HNSWLIB_STATUS_TYPE ::hnswlib::Status
218-
#define HNSWLIB_STATUS_OR_TYPE(t) ::hnswlib::StatusOr<t>
162+
#define HNSWLIB_STATUS_TYPE ::absl::Status
163+
#define HNSWLIB_STATUS_OR_TYPE(t) ::absl::StatusOr<t>
219164
#define HNSWLIB_THROW_ERROR(message) \
220-
return ::hnswlib::Status(message);
221-
#define HNSWLIB_RETURN_OK_STATUS return ::hnswlib::Status()
165+
return ::absl::UnknownError(message);
166+
#define HNSWLIB_RETURN_OK_STATUS return ::absl::OkStatus()
222167
#define HNSWLIB_RETURN_IF_VALUE_NOT_OK(status_or_value) do { \
223168
if (!status_or_value.ok()) { \
224169
return status_or_value.status(); \
225170
} \
226171
} while (false)
227172
#define HNSWLIB_EXTRACT_VALUE(status_or_value) (status_or_value).value()
228-
#define HNSWLIB_ASSIGN_VALUE_OR_THROW_IN_TEST_HELPER(identifier, expression, tmp_identifier) \
173+
174+
#define HNSWLIB_CONCAT_NAME_HELPER(x, y) x##y
175+
#define HNSWLIB_CONCAT_NAME(x, y) HNSWLIB_CONCAT_NAME_HELPER(x, y)
176+
#define HNSWLIB_MAKE_TMP_IDENTIFIER(identifier) \
177+
HNSWLIB_CONCAT_NAME(HNSWLIB_CONCAT_NAME(identifier, _tmp), __COUNTER__)
178+
179+
#define HNSWLIB_ASSIGN_OR_RETURN_HELPER(lhs, expression, tmp_identifier) \
180+
auto tmp_identifier = (expression); \
181+
if (!tmp_identifier.ok()) { \
182+
return tmp_identifier.status(); \
183+
} \
184+
lhs = std::move(tmp_identifier.value());
185+
186+
#define HNSWLIB_ASSIGN_OR_RETURN(lhs, expression) \
187+
HNSWLIB_ASSIGN_OR_RETURN_HELPER( \
188+
lhs, \
189+
expression, \
190+
HNSWLIB_MAKE_TMP_IDENTIFIER(status_or_value))
191+
192+
#define HNSWLIB_ASSIGN_VALUE_OR_THROW_IN_TEST_HELPER( \
193+
identifier, \
194+
expression, \
195+
tmp_identifier) \
229196
auto tmp_identifier = (expression); \
230197
if (!tmp_identifier.ok()) { \
231-
throw std::runtime_error(tmp_identifier.status().message()); \
198+
auto message = tmp_identifier.status().message(); \
199+
std::string message_str(message.data(), message.size()); \
200+
throw std::runtime_error(message_str); \
232201
} \
233202
auto identifier = std::move(tmp_identifier.value());
234-
#define HNSWLIB_MAKE_TMP_VAR_NAME(prefix, suffix) prefix##_##suffix
235203

236204
#define HNSWLIB_ASSIGN_VALUE_OR_THROW_IN_TEST(identifier, expression) \
237-
HNSWLIB_ASSIGN_VALUE_OR_THROW_IN_TEST_HELPER(identifier, expression, HNSWLIB_MAKE_TMP_VAR_NAME(identifier, __LINE__))
205+
HNSWLIB_ASSIGN_VALUE_OR_THROW_IN_TEST_HELPER( \
206+
identifier, \
207+
expression, \
208+
HNSWLIB_MAKE_TMP_IDENTIFIER(identifier))
238209
#endif
239210

240211
typedef size_t labeltype;
@@ -323,7 +294,7 @@ class AlgorithmInterface {
323294
BaseFilterFunctor* isIdAllowed = nullptr) const {
324295

325296
// Here searchKnn returns the result in the order of further first.
326-
HNSWLIB_ASSIGN_VALUE_OR_THROW_IN_TEST(ret, searchKnn(query_data, k, isIdAllowed));
297+
HNSWLIB_ASSIGN_OR_RETURN(auto ret, searchKnn(query_data, k, isIdAllowed));
327298

328299
DistanceLabelVector result;
329300
size_t sz = ret.size();

tests/cpp/sift_1b.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
#include <fstream>
33
#include <queue>
44
#include <chrono>
5+
#include <utility>
56
#include "../../hnswlib/hnswlib.h"
67

78

0 commit comments

Comments
 (0)