GitHub Copilot исправит баги в твоём APIПродолжаю исследовать возможности AI-ассистентов написания кода. Ранее на эту тему были
— пост про ожидания и реальность от использования Codeium в реальной кодовой базе
— видео с добавлением новой фичи с помощью GitHub Copilot в большую кодовую базу (
😉 YouTube,
😄 ВКонтакте)
А на прошлой неделе GitHub Copilot удивил меня тем, что обнаружил баг в API класса, который я написал. Сегодня хочу об этом рассказать.
Для одного тестового задания мне понадобилось написать аллокатор поверх кольцевого буфера: запрашиваешь у него сколько-то байт, и он выделяет их с "конца" буфера. Освобождать память можно только с "начала" буфера. Сперва я сделал вот такой интерфейс:
class RingBufferAllocator {
public:
explicit RingBufferAllocator(std::span<char> arena);
struct Iterator { ... }; // Абстрагирует переход через край буфера
std::optional<Range<Iterator>> Allocate(size_t bytes);
void Deallocate(Range<Iterator> range);
};
Метод
Allocate
возвращает пару итераторов либо
std::nullopt
, если в буфере недостаточно места. И мне показалось логичным в
Deallocate
передавать тот
Range
, который вернулся из
Allocate
.
После этого я стал писать тесты на
RingBufferAllocator
и попросил Copilot сделать это за меня. Первый же его тест выявил ошибку в моём API:
TEST(RingBufferAllocatorTest, AllocateAndDeallocate) {
std::array<char, 16> arena; // Example arena with 16 bytes
RingBufferAllocator allocator(arena);
// Allocate 8 bytes
auto range1 = allocator.Allocate(8);
ASSERT_TRUE(range1.has_value());
EXPECT_EQ(range1.value().size(), 8);
// Allocate 4 bytes, total 12 bytes allocated
auto range2 = allocator.Allocate(4);
ASSERT_TRUE(range2.has_value());
EXPECT_EQ(range2.value().size(), 4);
allocator.Deallocate(*range2); // <--- OOPS. THE BUG IS HERE
auto range3 = allocator.Allocate(8);
ASSERT_TRUE(range3.has_value());
EXPECT_EQ(range3.value().size(), 8);
}
Ошибка в том, что мой API позволял освобождать память не только с "начала" буфера, но и внутри. А это противоречит идее кольцевого буфера. Заметив это, я поменял API вот так:
class RingBufferAllocator {
public:
explicit RingBufferAllocator(std::span<char> arena);
struct Iterator { ... };
// Tries to allocate from the end of the ring buffer
std::optional<Range<Iterator>> Allocate(size_t bytes);
// Deallocates the first `bytes` bytes from the beginning of the ring buffer
void Deallocate(size_t bytes);
};
Теперь
Deallocate
тоже принимает количество байт и освобождает их только с "начала" буфера.
Прелесть этой истории в том, что Copilot помог мне найти ошибку почти сразу после её допущения и не дать плохому API расползтись по коду
💪В следующем посте расскажу ещё одну историю, как мне неожиданно помог Copilot. А сейчас поделитесь, какую пользу от AI-ассистентов написания кода извлекаете вы.