Merhabalar.
Henüz yeni C öğrenmeye başladım. Low-level programlama adına Rust’tan öteye hiç gitmedim daha önce. Yani özellikle memory management konusunda öğreneceğim çok şey var.
Pratik olması açısından lichess.org’da zaman kontrollerine göre en iyi 10 satranç oyuncularını gösteren bir program yazmak istedim. Github reposu üzerinden kodları okumak isterseniz: Github linki
Şimdi neler yaptığımı ve neden bu konuyu açtığımı anlatayım.
Aşağıdaki kod fetch_players.h
:
#ifndef FETCH_PLAYERS_H
#define FETCH_PLAYERS_H
#define API "https://lichess.org/api/player"
void fetch_top_players();
#endif
fetch_players.c
:
main.c’de bu modülün içindeki fetch_top_players()
fonksiyonunu çağırıp lichess’te zaman kontrollerine göre en yüksek elolu satranç oyuncularını printlemek istiyorum.
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <curl/curl.h>
#include "fetch_players.h"
#include "json-parser/parser.h"
size_t write_callback(void *incoming_data, size_t size_of_each_unit, size_t number_of_units, void *user_provided_buffer)
{
size_t total_data_size = size_of_each_unit * number_of_units;
char **response_buffer = (char **)user_provided_buffer;
if (!(*response_buffer))
{
*response_buffer = (char *)malloc(total_data_size + 1);
if (!(*response_buffer))
{
fprintf(stderr, "Memory allocation failed\n");
return 0;
}
}
memcpy(*response_buffer, incoming_data, total_data_size);
(*response_buffer)[total_data_size] = '\0';
return total_data_size;
}
void fetch_top_players()
{
CURL *curl = NULL;
CURLcode res;
char *response_buffer = NULL;
curl_global_init(CURL_GLOBAL_DEFAULT);
curl = curl_easy_init();
if (curl)
{
curl_easy_setopt(curl, CURLOPT_URL, API);
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_callback);
curl_easy_setopt(curl, CURLOPT_WRITEDATA, &response_buffer);
res = curl_easy_perform(curl);
if (res != CURLE_OK)
{
fprintf(stderr, "curl_easy_perform() failed: %s\n", curl_easy_strerror(res));
}
curl_easy_cleanup(curl);
}
curl_global_cleanup();
if (response_buffer)
{
parse_json_response(response_buffer);
free(response_buffer);
}
}
Bir de parser.c
var:
Burada da json response’u parse edip oyuncuları sıralamayı planlıyorum. Şimdilik sadece argüman olarak verilen json response’u print ediyor. Daha sonra geliştirmeye çalışacağım burayı.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <curl/curl.h>
#include "parser.h"
void parse_json_response(char *json)
{
printf("Response value:\n");
printf("----\n");
printf("%s\n", json);
}
fetch_players.c
'de curl kullanarak lichess apisine request atıyorum. Yalnız buradaki gibi bir curl kodu sadece request atıp response’u terminalde göstermeye yarıyor. Ben de response’u daha sonra parse_json_response()
gibi fonksiyonlar yazıp işleyebilmek için bunu bir değişkene aktarmam gerektiğini düşündüm.
Bu noktada tıkanınca yapay zekadan kod istedim. Bir callback function yazdı: write_callback()
. Sonra da bu fonksiyondan yararlanarak curl’den gelen response’u bir buffer’da tutabilmek için bu kodu yazdı:
curl_easy_setopt(curl, CURLOPT_URL, API);
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_callback);
curl_easy_setopt(curl, CURLOPT_WRITEDATA, &response_buffer);
Bu sayede artık response bir buffer’da (tampon?) tutulduğu için artık onu bir string olarak işleyebilecektik. Her şey gayet mantıklı görünüyor.
Kodun mantığını aşağı yukarı anladım. Sadece
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_callback);
burada curl_easy_setopt()
'nin write_callback()
'i nasıl kullandığını biraz merak ediyorum. Neyse, asıl konu bu değil.
Şimdi yapay zeka (chatgpt) mantıklı ve işe yarayan bir çözüm sunmuş gibi görünüyor, kodun mantığını (sonuç olarak neyi sağladığını kastediyorum) da anladım genel olarak ancak kodun işleyişi hakkında, kodun kendisi hakkında soru işaretlerim oldu.
size_t write_callback(void *incoming_data, size_t size_of_each_unit, size_t number_of_units, void *user_provided_buffer)
{
size_t total_data_size = size_of_each_unit * number_of_units;
char **response_buffer = (char **)user_provided_buffer;
if (!(*response_buffer))
{
*response_buffer = (char *)malloc(total_data_size + 1);
if (!(*response_buffer))
{
fprintf(stderr, "Memory allocation failed\n");
return 0;
}
}
memcpy(*response_buffer, incoming_data, total_data_size);
(*response_buffer)[total_data_size] = '\0';
return total_data_size;
}
Yeni yeni C öğrendiğimi de düşünürsek bu kodu okumakta epey zorlandım doğrusu. Pek insansı bir kod gibi de durmuyor sanki, pek doğal durmuyormuş gibi. Ya da idiomatic C kodu değilmiş gibi. Yeni öğrenen biri olarak bunu söylemem pek de mantıklı değil elbette ama bir şekilde şüphe duyuyorum sadece. Bu arada yapay zeka başta statik bir buffer tanımlamıştı char[4096]
gibi. Stack smash...
gibi bir hata yükseltilmişti. Anlaşılan o ki buffer’ın genişliği biraz az gelmiş. Daha sonra dynamic memory management yapan bir program için promptlar atınca şu anki halini aldı.
Sonuç olarak kod karmaşık geldiği için foruma bir danışmak istedim. Curl’den gelen response’u işlemek için yapay zekanın yaptığı gibi buffer tutmak iyi bir practice mi yoksa aynı mantığı daha insansı ve idiomatic bir şekilde işlemek mümkün mü? Mümkünse nasıl bir şey yazılabilir? Ve ayrıca yapay zekanın yazdığı kodu da benim için detaylı bir şekilde analiz edebilir misiniz? Son olarak repoda ayrıca iyileştirebileceğim bir şeyler varsa bunlardan da bahsederseniz memnun olurum. Makefile’ımı da yapay zekaya yazdırmıştım mesela ama belki de olması gerekenden daha gereksiz bir şekilde karmaşık olmuştur basit bir repo için.
Doğal/insansı ya da idiomatic koddan kastım bazen yapay zekanın basit bir iş için olağandan daha karmaşık şeyler yazması ile ilgili. İnsanın aklına ilk gelen genelde daha sade, daha basit bir yöntem olabiliyor. Yapay zeka ise olur olmadık işleri karmaşıklaştırabiliyor. Satrançta insanın kolaylıkla kazanca giden yolu seçerken yapay zekanın ortada basit bir kazanç yolu olmasına rağmen en en iyi yoldan kazanmayı karmaşıklaştırması gibi bir benzerlik kurulabilir belki de. Yani amacım daha çok kod hakkında analizlerinizi görüp kendimi geliştirmek; aynı amaca hizmet eden daha iyi bir yöntem varsa bunu öğrenmek, şu anki kod yeterince iyiyse de tam olarak nasıl çalıştığını anlamak. Pointerlar, memory işlemleri vb. low level konseptlerde henüz acemi sayılırım. Bunu da göz önünde bulundurunca sizlerin bilgilerinden istifade etmek istedim.
Not: Konu için aklınıza daha iyi bir başlık gelirse değiştirebilirsiniz.