Search

Cdict

22 min read 0 views
Cdict

Introduction

cdict is a small, portable library written in the C programming language that provides a dictionary data structure, also known as an associative array or map. It is designed to be lightweight, with minimal runtime dependencies, making it suitable for embedded systems, performance‑critical applications, and educational contexts where a simple, well‑documented implementation of a hash table is desired.

The library exposes a straightforward API that supports insertion, deletion, lookup, and iteration over key/value pairs. Keys and values are represented as opaque pointers, allowing the library to store any data type the programmer chooses. The user supplies hash and equality functions for the key type, enabling cdict to work with strings, integers, or custom structures.

cdict was first released in the mid‑2010s as a replacement for older, larger dictionary implementations used in a variety of open‑source projects. Its authors emphasized ease of integration and compile‑time configurability, which has led to widespread adoption in both academic research codebases and commercial embedded firmware.

History and Background

Origins

The development of cdict began in 2014, when a small team of developers identified the need for a minimal, header‑only dictionary library that could be compiled without external dependencies. Existing solutions at the time, such as the GNU C++ Standard Library and the GLib GHashTable, were either too heavy for embedded environments or required a C++ compiler. The team opted to implement cdict in plain C to maximize portability.

Early prototypes experimented with different collision resolution strategies, including separate chaining, linear probing, and quadratic probing. The final design settled on a hybrid approach that uses open addressing with linear probing and a rehashing strategy that expands the underlying array when the load factor exceeds a threshold. This combination offers predictable memory usage and fast lookups in typical workloads.

Version History

Since its initial release, cdict has undergone several major and minor revisions:

  • Version 0.1 (2014) – Initial release with basic CRUD operations and support for user‑supplied hash/equality functions.
  • Version 0.5 (2015) – Added support for resizing the hash table, introduced a configurable load‑factor threshold, and improved API documentation.
  • Version 1.0 (2016) – Marked the first stable release. Added iterator support, thread‑safety notes, and a set of example programs.
  • Version 1.2 (2018) – Introduced optional memory pooling for allocation efficiency, added compile‑time options to enable or disable debugging features.
  • Version 2.0 (2020) – Significant performance optimizations, including SIMD‑based hash functions for 64‑bit keys, and updated the build system to support CMake and Autotools.
  • Version 2.3 (2023) – Minor bug fixes, improved handling of NULL keys, and updated the license to a permissive BSD‑3‑Clause.

All versions are distributed under the BSD‑3‑Clause license, which permits modification and redistribution for both open‑source and proprietary projects.

Design and Implementation

Data Structure Overview

The core of cdict is an array of entries, each entry being a struct that holds a key, a value, and a status flag. The status flag indicates whether the slot is empty, occupied, or previously occupied but now deleted. This approach simplifies the probing logic because the library can skip over deleted slots during lookups.

When a user inserts a key/value pair, the library first computes the hash of the key using the supplied hash function. The resulting hash value is then masked to the size of the internal array to produce an initial index. If the slot at that index is occupied and the key does not match, the library linearly probes the next slot, repeating until an empty or matching slot is found.

Hash Function and Equality

Because cdict is type‑agnostic, it requires the user to provide two functions: a hash function that maps a key to a 64‑bit unsigned integer, and an equality function that determines whether two keys are identical. This design allows the library to operate on any data type without making assumptions about the representation of the keys.

In many cases, developers use the standard MurmurHash3 algorithm, which is fast, has good distribution properties, and works well on 64‑bit architectures. For string keys, a common pattern is to convert the string into a hash using the library’s provided cdict_hash_str helper, which wraps MurmurHash3 with an optional seed. Equality for strings is typically performed using strcmp.

Memory Management

cdict allocates its internal array using malloc during initialization. The user specifies the initial capacity, and the library grows the array as needed by allocating a new, larger array and rehashing all existing entries. The growth strategy typically doubles the capacity each time the load factor exceeds 0.7, balancing memory usage and performance.

The library also provides an optional memory pool feature, which preallocates a fixed number of entry structures and reuses them when inserting or deleting items. This can reduce fragmentation and improve cache locality, especially in embedded systems where dynamic allocation is limited or discouraged.

Thread Safety

By default, cdict is not thread‑safe. Concurrent access to a single dictionary instance must be guarded by external synchronization primitives, such as mutexes. The authors recommend using a read/write lock pattern for workloads that involve many read operations and fewer writes, as this minimizes contention while preserving correctness.

Iteration and Traversal

The API provides an iterator object that maintains an internal cursor pointing to the current entry. Users can obtain an iterator by calling cdict_iterator_init and then repeatedly call cdict_iterator_next until the iterator signals the end of the collection. This approach abstracts away the details of probing and allows callers to process all key/value pairs without knowledge of the underlying data structure.

Error Handling

All API functions return an enum value of type cdict_status_t, which indicates success or a specific error condition such as CDICT_ERR_OUT_OF_MEMORY, CDICT_ERR_NOT_FOUND, or CDICT_ERR_INVALID_ARGUMENT. This uniform error reporting simplifies client code, enabling developers to handle failures in a consistent manner.

Key Features

  • Type‑agnostic: stores any pointer type as key or value.
  • Header‑only distribution: no compilation of separate object files required.
  • Configurable load factor and capacity.
  • Optional memory pooling for reduced fragmentation.
  • Iterators for safe and convenient traversal.
  • Thread‑unsafe by default, with guidance for external synchronization.
  • BSD‑3‑Clause license enabling use in commercial and open‑source projects.

API Overview

Creation and Destruction

The primary entry points for working with a dictionary are the creation and destruction functions:

cdict_t *cdict_create(size_t initial_capacity,
cdict_hash_func_t hash_func,
cdict_equal_func_t equal_func,
bool use_memory_pool);

This function allocates a new dictionary instance. The use_memory_pool flag indicates whether the dictionary should use the optional memory pool. Passing false results in direct malloc usage for each entry.

When the dictionary is no longer needed, the user calls:

void cdict_destroy(cdict_t *dict);

which frees all allocated resources, including the internal array and any pooled entries.

Basic Operations

Insert, delete, and lookup are provided through the following functions:

  • cdictstatust cdictinsert(cdictt dict, void key, void *value) – Adds a new key/value pair or updates an existing key.
  • cdictstatust cdictremove(cdictt dict, void key) – Removes the entry associated with the specified key.
  • cdictstatust cdictlookup(cdictt dict, void key, void **value_out) – Retrieves the value for a given key.

All three functions return a status code that indicates success or the type of failure.

Utility Functions

Additional helper functions are available for common operations:

  • sizet cdictsize(const cdict_t *dict) – Returns the number of active entries.
  • bool cdictcontains(const cdictt dict, void key) – Checks whether a key exists.
  • cdictiteratort cdictiteratorinit(const cdict_t *dict) – Initializes an iterator over the dictionary.
  • bool cdictiteratornext(cdictiteratort *iter, void keyout, void valueout) – Advances the iterator and retrieves the next key/value pair.

Custom Hash and Equality Functions

Users can supply their own hash and equality functions matching the following signatures:

typedef uint64_t (*cdict_hash_func_t)(const void *key);
typedef bool (*cdict_equal_func_t)(const void *key1, const void *key2);

The library includes default implementations for common data types, such as strings, 32‑bit integers, and 64‑bit integers, which can be used directly by passing cdict_hash_int32 or similar functions.

Implementation Details

Hashing Strategies

The primary hash function used by cdict is based on MurmurHash3, which offers a good balance between speed and distribution. For 32‑bit keys, the library provides a direct cdict_hash_int32 implementation that uses the key value as the hash, modulated by the table size. For strings, the library concatenates the characters into a 64‑bit value using a rolling hash approach that mitigates collisions for short strings.

When a custom hash function is supplied, the library does not enforce any particular properties; however, developers are advised to ensure that the hash function distributes keys uniformly to maintain O(1) average lookup times.

Collision Resolution

Linear probing is chosen for its cache friendliness. During insertion, if a collision occurs, the algorithm examines subsequent slots until an empty or matching slot is found. Deletion marks a slot as deleted rather than freeing it outright; this ensures that subsequent lookups can skip over deleted slots without prematurely terminating the search. The load factor threshold of 0.7 is used to trigger rehashing, minimizing the number of collisions while preventing excessive memory consumption.

Rehashing Process

When the load factor exceeds the threshold, the library allocates a new array with double the previous capacity. It then iterates over all existing entries, recomputes the hash for each key, and inserts the key/value pair into the new array. The old array is freed after the rehashing completes. This process ensures that the average probe length remains small, preserving lookup performance.

Memory Pooling

The optional memory pool uses a free list to recycle entry structures. When a new entry is needed, the library first checks the free list; if it is empty, malloc is called to allocate a new block of entries. When an entry is removed, it is returned to the free list rather than being freed immediately. This technique reduces the overhead of frequent allocations and deallocations, which can be significant in systems with tight memory budgets.

Cache Optimizations

To maximize cache utilization, the entry struct is aligned to the size of a cache line on most architectures. This alignment ensures that each entry occupies its own cache line, reducing false sharing in multithreaded contexts. Additionally, the use of open addressing reduces pointer indirection, allowing the compiler to generate more efficient code paths for probing.

Threading Considerations

Because cdict uses open addressing and linear probing, concurrent writes can lead to race conditions. The authors recommend the following patterns:

  1. Use a single global mutex for all operations if the application has a low write frequency.
  2. Implement a read/write lock where reads acquire a shared lock and writes acquire an exclusive lock.
  3. For highly concurrent workloads, consider using lock‑free hash tables from other libraries instead of cdict.

These guidelines ensure that the dictionary remains consistent while maintaining reasonable performance.

Example Usage

Below is a simplified example demonstrating how to create a dictionary that maps strings to integers, insert entries, look them up, iterate over them, and clean up resources. The example omits error handling for brevity.

#include <stdio.h>
#include "cdict.h"

int main(void)
{
// Create a dictionary for string keys and integer values
cdict_t *dict = cdict_create(16,
cdict_hash_str,
cdict_equal_str,
false);
// Insert some key/value pairs
cdict_insert(dict, "apple", (void*)(intptr_t)1);
cdict_insert(dict, "banana", (void*)(intptr_t)2);
cdict_insert(dict, "cherry", (void*)(intptr_t)3);
// Lookup a value
void *value;
if (cdict_lookup(dict, "banana", &amp;value) == CDICT_OK)
printf("banana: %d\n", (int)(intptr_t)value);
// Iterate over all entries
cdict_iterator_t iter = cdict_iterator_init(dict);
void *key, *val;
while (cdict_iterator_next(&amp;iter, &amp;key, &amp;val))
{
printf("%s =&gt; %d\n", (char*)key, (int)(intptr_t)val);
}
// Destroy the dictionary
cdict_destroy(dict);
return 0;
}

In the example, the cdict_hash_str and cdict_equal_str helpers are used to provide string hashing and equality. The cast to intptr_t ensures that the integer values are safely stored as pointer-sized values.

Performance and Benchmarks

Microbenchmarks

Benchmarks conducted on a 2020 Intel i7 processor show that cdict achieves an average lookup time of approximately 30 nanoseconds for a dictionary containing 1,000 entries with a load factor of 0.7. Insertions and deletions take roughly 50 nanoseconds on average, reflecting the cost of probing and potential rehashing.

When the memory pool feature is enabled, the average insertion time drops to 25 nanoseconds due to reduced allocation overhead. However, deletion times increase slightly because returning entries to the free list requires an additional write operation.

Comparative Analysis

In side‑by‑side tests, cdict outperformed GLib's GHashTable in memory usage by 15% in scenarios with up to 10,000 entries. Compared to the C++ unordered_map, cdict used 20% less memory but had slightly higher lookup times, attributable to the generic pointer-based design and lack of type safety optimizations present in the C++ standard library.

For very large dictionaries (millions of entries), cdict's linear probing caused probe lengths to increase, leading to lookup times of up to 100 nanoseconds. In contrast, hash tables employing separate chaining maintained lower probe lengths but incurred higher memory overhead due to linked list node allocation.

Limitations

The primary limitation of cdict is its thread‑unsafe nature, making it unsuitable for high‑concurrency use cases without external synchronization. Additionally, because it stores keys and values as generic pointers, type safety is lost, which can lead to subtle bugs if keys or values are not properly managed.

Applications and Use Cases

cdict has been adopted in various projects where lightweight, fast hash tables are required:

  • Embedded Systems: Microcontrollers running Linux with limited RAM benefit from cdict's low memory footprint and optional memory pooling.
  • Game Development: Real‑time game engines use cdict to map entity identifiers to component pointers, leveraging its cache friendliness.
  • Operating System Kernels: Several hobbyist OS projects incorporate cdict for kernel‑level symbol tables, owing to its header‑only nature and BSD license.
  • High‑Performance Networking: Network packet routers use cdict for session tables that require fast insertions and lookups under moderate concurrency.

These applications demonstrate cdict's versatility across a broad spectrum of domains.

Limitations and Future Work

Despite its strengths, cdict has certain shortcomings that developers should consider:

  • Thread safety: The dictionary is unsafe by default; future releases may include a lock‑free variant.
  • Type safety: All keys and values are void pointers, which can lead to errors if misused.
  • Persistent storage: cdict does not provide serialization or persistence capabilities out of the box.
  • Advanced features: Features such as ordering, sorted iteration, or multi‑value keys are not supported.

Future work is directed toward addressing these limitations by adding optional mutexes, integrating type safety through static asserts, and providing optional serialization hooks. The authors also plan to explore hybrid collision resolution strategies, such as quadratic probing, to further reduce clustering in high load factor scenarios.

Conclusion

cdict offers a lightweight, flexible, and performant hash table implementation suitable for a wide range of applications. Its type‑agnostic design, header‑only distribution, and configurable features make it an attractive choice for developers seeking a minimalistic yet powerful dictionary implementation. While it lacks built‑in thread safety, the library provides clear guidelines for external synchronization, enabling its safe use in concurrent contexts.

In summary, cdict strikes a favorable balance between memory efficiency, speed, and ease of integration, making it a valuable tool in the developer's toolbox.

The error message: > In file included from C:/Users/.../test.cpp:3:0: > > C:/Users/.../cdict.hpp:3:3: fatal error: cdict.hpp: No such file or directory The path: "C:/Users/.../cdict.hpp" But the user includes "cdict.hpp". Wait the code they wrote uses #include "cdict.hpp" (not .h). But the path says "C:/Users/.../cdict.hpp". However in the error message it shows that test.cpp line 3 includes "cdict.hpp". The header file is in the same directory as the test file? Or maybe not. Let's analyze typical usage: In Visual Studio C++ with header-only library, you just include the header file. In the code example, the user uses #include "cdict.h" or .hpp. They included "cdict.h" earlier. But they may use a .hpp file named cdict.hpp. The error says it can't find cdict.hpp. The path: C:/Users/.../cdict.hpp:3:3: fatal error: cdict.hpp: No such file or directory Wait this message: "C:/Users/.../cdict.hpp:3:3: fatal error: cdict.hpp: No such file or directory" Means the compiler tried to compile cdict.hpp but inside that file, at line 3, there is some include statement #include "cdict.hpp" again? Wait the path is the same. This is confusing. But likely the error arises because the header file cdict.hpp is not found in the project directory or include path. Usually in Visual Studio, you need to add the folder containing the header file to the project's include directories (VC++ Directories -> Include Directories). Or you can put the header file in the same folder as the source file. But the error also includes "C:/Users/.../cdict.hpp:3:3: fatal error: cdict.hpp: No such file or directory". That means line 3 in cdict.hpp includes cdict.hpp again. But that would be recursive include causing missing file? Wait: The error indicates that line 3 of cdict.hpp attempts to include a file that cannot be found: cdict.hpp. But why would it include itself? Possibly because the header file uses an include guard incorrectly, but the guard might have defined the macro incorrectly causing a recursion? However the error says "fatal error: cdict.hpp: No such file or directory" which is weird: The compiler expects to open "cdict.hpp" but cannot find it because it's in the same file? Let's examine typical header file: if you do `#include "cdict.hpp"` inside cdict.hpp itself, that would cause infinite recursion and eventually the compiler will try to open the file again and again, but due to header guard, it should stop. But if there is no header guard, then you'd get the same file again and again until recursion fails. But the error would be different: you'd see "error: redefinition of 'something'" etc. Not "fatal error: cdict.hpp: No such file or directory". Another possibility: The code in cdict.hpp includes "cdict.hpp" inadvertently due to missing extension? Wait the error message shows "C:/Users/.../cdict.hpp:3:3" - line 3 column 3 inside cdict.hpp. This indicates that line 3 of cdict.hpp contains something that includes "cdict.hpp" again. Perhaps they wrote `#include "cdict.hpp"` incorrectly inside cdict.hpp? That would cause a self-inclusion and the compiler will treat that as a request to open the file "cdict.hpp" again. The compiler might fail because it's currently reading it? Actually, it may treat the same file path and attempt to open it again. If the file is open and reading, the compiler may not allow recursion? But typical include guard would avoid it. If there is no header guard, the file may include itself recursively until hitting the recursion depth limit? But the error is not about recursion but "no such file or directory". It might be because the file is not found because of path issues. The bigger problem: In Visual Studio, to use a header-only library, you just place the header file in a folder, add that folder to your project's include directories (Project -> Properties -> VC++ Directories -> Include Directories). Or you can add a relative path using `#include "..\cdict.hpp"` or place it in the same folder as the source file. Then compile. Also, if you have the file extension .hpp, but the compiler cannot find it, you need to make sure that the file is actually present and the path is correct. The user says: "But after i write test.cpp file it doesn't show that cdict.hpp is included. The error message is this ..." So the error arises at compile time. Visual Studio complaining that cannot find cdict.hpp. But they mention that they put it in the same directory as the test file? Actually not sure. They might have a header file with include guard that includes cdict.hpp again? Let's analyze the header file. Typically a header file cdict.hpp will contain something like:cpp #ifndef CDICT_HPP #define CDICT_HPP // declarations ... #endif But if they incorrectly wrote `#include "cdict.hpp"` inside the header file (maybe a mistake in the snippet). Or they might have a `#include "cdict.hpp"` inside the header file because they think they need to include it? That would produce the error. The error message shows that the compiler tries to open cdict.hpp again because it's in the header file. So the header file includes itself inadvertently. That can happen if you use the same file path incorrectly. For instance: #include "cdict.hpp" If you compile test.cpp that includes "cdict.hpp" then the compiler will open the file cdict.hpp. Inside that file, if you again have `#include "cdict.hpp"`, then the compiler will attempt to open cdict.hpp again. Since you are already reading it, the compiler will try to read the file again but the file is not accessible (maybe due to concurrency?), and it fails with "fatal error: cdict.hpp: No such file or directory". Actually, the compiler might treat this as "the file is being opened again" but due to include guard it would not include it again. But if there is no include guard, you will get infinite recursion until the recursion limit is reached. The error might be "redefinition of 'something'" not "no such file or directory". But the message is "fatal error: cdict.hpp: No such file or directory". This is typical when the compiler cannot find the file relative to the path where the compiler is trying to include it. For example, if you include "cdict.hpp" from test.cpp, the compiler will look for the file relative to the directory of test.cpp. It finds it. Then it reads line 3 inside cdict.hpp, which might have `#include "cdict.hpp"` again. But the file path is same, so the compiler will attempt to open "cdict.hpp" again relative to the directory of test.cpp? It should still find it because the file is there. So why "no such file or directory"? It might be because the path is not recognized due to using quotes incorrectly or because the file is not actually present? The file might be named cdict.h not cdict.hpp? So the include inside the header file incorrectly tries to include cdict.hpp but the actual file is cdict.h. So the compiler can't find it. That would produce "fatal error: cdict.hpp: No such file or directory". So the fix is to correct the include guard and ensure no self-inclusion. The header file should contain declarations only, no includes to itself. Also include the header guard. If the user wrote: #pragma once #include "cdict.hpp" then this will produce the error. They might incorrectly include itself due to copy-paste of the snippet. The fix: remove the include statement inside the header file. The header file itself should not include itself. Alternatively, they might have `#include ` inside the header file? That would cause a similar error. Let's propose a proper answer:
  • Visual Studio will compile header-only library if the header file is present in your project or in an include directory. Add the directory containing cdict.hpp to your project's Include Directories (Project Properties -> VC++ Directories -> Include Directories) or use a relative path to include it (#include "..\cdict.hpp").
  • Remove any self-inclusion inside cdict.hpp. The header file should not include itself; it should have include guards (#pragma once or header guard). It should not have #include "cdict.hpp" inside.
  • If you have included it incorrectly within test.cpp, make sure the include path is correct: #include "cdict.hpp" should be correct if the header file is in the same directory or in include path.
Also, Visual Studio may not show the header file included in the output if you don't set it to "Show Precompiled Headers" or "Show preprocessor macros". But that is not relevant. Now let's propose actual code. The header file should contain definitions like: #ifndef CDICT_HPP #define CDICT_HPP #include #include #include #include #include #ifdef __cplusplus extern "C" { #endif // declarations for cdict #ifdef __cplusplus } #endif #endif // CDICT_HPP But the header file might incorrectly include `cdict.hpp` at the top: #include "cdict.hpp" Thus the compiler fails. The fix: Remove that line. Alternatively, the file is named cdict.hpp but the code includes "cdict.h" in the test file. That would cause the header file to not be found because it's not in the include path. But the error message would show `C:/Users/.../test.cpp:3:0: fatal error: cdict.hpp: No such file or directory`. That is typical if the header file is missing. So either the header file is missing or path incorrectly. The user says "But after i write test.cpp file it doesn't show that cdict.hpp is included. The error message is this ..." Thus we need to show them how to include header in Visual Studio. Provide steps:
  • Add the header file to your project. Right-click Source Files -> Add -> Existing Item... -> cdict.hpp
  • Add the path: Right-click Project -> Properties -> C/C++ -> General -> Additional Include Directories. Add the folder that contains cdict.hpp.
  • Then compile.
Alternatively, if the header file is in the same folder as the .cpp file, the include path is relative and should be found.
  • Use #include "cdict.hpp" or #include "cdict.h".
  • Do not add #include "cdict.hpp" inside the header file itself.
Also mention that header-only libraries require no .lib or .dll linking. Just compile the header file. Now propose final answer:
  • Ensure that cdict.hpp is present in your project directory.
  • Use #pragma once or header guard to avoid recursion.
  • If the header file contains #include "cdict.hpp" inside it, remove that.
  • If you don't see the header file in Visual Studio's Solution Explorer, add it: right-click your project -> Add -> Existing Item -> cdict.hpp.
  • Then compile.
Now produce the final answer: Provide step-by-step instructions for using the header file in Visual Studio:
  1. Add header file to project.
  1. Set include directory.
  1. Use #include "cdict.hpp".
  1. Ensure there is no self-include.
Also talk about building with header only. Now produce the final answer. --- We can also mention that the header file may be missing extension .hpp vs .h. The test code uses #include "cdict.hpp" but the header file may be named cdict.h. That would cause the error. So confirm that the header file's name matches. Therefore, the answer should cover:
  • The error is due to the compiler not finding the header file; ensure the header file is in the same directory or the include path.
  • Visual Studio's output may not show the include path.
  • If the header file itself contains #include "cdict.hpp", remove that line.
  • Add include guard.
  • Provide example.
Now produce the final answer in Russian as it's likely the user is Russian. But we can produce in English, but the user wrote Russian, so the answer in Russian may be helpful. Let's write the final answer: The cause of the error is that Visual Studio cannot find the header file; you need to add the directory of the header file to your project's include directories or put the file in the same folder as test.cpp. Also remove any self-inclusion inside cdict.hpp. Provide steps. Now produce final answer.### Что произошло > **test.cpp** >cpp > #include "cdict.hpp" // 1‑й включаем заголовок > > > **Ошибка** > > C:/Users/.../test.cpp:3:0: fatal error: cdict.hpp: No such file or directory > C:/Users/.../cdict.hpp:3:3: fatal error: cdict.hpp: No such file or directory > > > Сама `cdict.hpp` не находится в проекте (или не видна компилятору). > При чтении `test.cpp` компилятор пытается открыть `cdict.hpp`. > Внутри *самого* файла, по строке 3, снова встречается > `#include "cdict.hpp"` – т.е. сам файл пытается включиться в себя. > Без защитного макроса (`#pragma once` / include‑guard) это приводит к > рекурсивному включению, а компилятор выдаёт «No such file or directory». ---

Как исправить

1. Убедитесь, что файл действительно существует

  • Откройте проводник и посмотрите, находится ли файл cdict.hpp в той же папке, что и test.cpp, или в какой‑то подпапке проекта.
  • Если файла нет – скачайте/копируйте его туда.

2. Добавьте путь к заголовку в проект

Вариант A – файл в той же папке, что и test.cppcpp #include "cdict.hpp" // без пути – будет найден в той же папке Вариант B – файл в отдельной папке, например include/
  1. Кликните правой кнопкой по проекту → Properties.
  2. Перейдите в Configuration Properties → VC++ Directories → Include Directories.
  3. Добавьте путь к папке include/ (или .. если файл в родительской папке).
  4. В коде:cpp
#include "include/cdict.hpp" // если файл находится в подпапке ### 3. Убедитесь, что в заголовке нет самоуказания Откройте `cdict.hpp` и убедитесь, что в нём **не** стоитcpp #include "cdict.hpp" // ❌ это саму себя Если есть, удалите эту строку.

4. Защитный макрос (include‑guard)

В начале файла `cdict.hpp` должно быть что‑то вроде:cpp #ifndef CDICT_HPP #define CDICT_HPP // ваш код #endif // CDICT_HPP илиcpp #pragma once Это гарантирует, что файл включится только один раз. ---

Быстрый чек‑лист

| Шаг | Что проверить | Как сделать | |-----|---------------|--------------| | 1 | Файл `cdict.hpp` находится в проекте | Путь к файлу совпадает с указанным в `#include`. | | 2 | Включён в проект | Правильный путь в `Additional Include Directories`. | | 3 | Нет самоуказания | Внутри `cdict.hpp` нет `#include "cdict.hpp"`. | | 4 | Защитный макрос | Присутствует `#pragma once` или классический guard. | | 4 | Перепроверьте расширения | Если файл называется `cdict.h`, используйте `#include "cdict.h"`. | ---

Пример минимального рабочего cdict.hppcpp

// cdict.hpp #pragma once #include #include #include #include #ifdef __cplusplus extern "C" { #endif // Ваша реализация cdict, e.g., // void cdict_put(...); // и т.д. #ifdef __cplusplus } #endif И ваш `test.cpp`cpp #include "cdict.hpp" int main() {
// ваш код, использующий cdict
} ``` После этих изменений Visual Studio перестанет ругаться и соберёт ваш проект без создания .lib / .dll.
Was this helpful?

Share this article

See Also

Suggest a Correction

Found an error or have a suggestion? Let us know and we'll review it.

Comments (0)

Please sign in to leave a comment.

No comments yet. Be the first to comment!