Hur använder man const char* men ändå måste ändra den? C++

Permalänk

Hur använder man const char* men ändå måste ändra den? C++

Jag har en liten fråga.
Jag vill utveckla mig inom C++ och skriva mindre C++ kod igenom att använda funktioner och templates. Men jag stöter på lite problem.

Jag använder ImGui för att göra grafiska applikationer i C++. Det är ett enkelt verktyg och resultatet blir väldigt bra. Men som sagt så måste man ha bra koll på minneshanteringen för arrayer.

void createCombo(const char* label, const char* items[], int length_items, const char* selected_item, int* selected_index) { if (ImGui::BeginCombo(label, selected_item)) { for (int i = 0; i < length_items; i++) { bool is_selected = (selected_item == items[i]); // You can store your selection however you want, outside or inside your objects if (ImGui::Selectable(items[i], is_selected)) { selected_item = const_cast<char*>(items[i]); *selected_index = i; std::cout << selected_item << std::endl; } } ImGui::EndCombo(); } }

Problemet med denna kod är att selected_item är en konstant. I detta fall så är den initialiserad med "not selected". const betyder att jag kan ändra den i funktionen, men när jag lämnar funktionen så kommer selected_item återgå till sitt ursprungliga värde.

Med andra ord så har denna ringa effekt.

selected_item = const_cast<char*>(items[i]);

Jag får den att kopiera över data, men att kunna behålla data, fungerar absolut inte.

I detta fall anropar jag koden som

const char* baudrates_char[] = {"110", "150", "300", "600", "1200", "1800", "2400", "4800", "9600", "19200", "38400", "57600", "115200"}; const int baudrates_int[] = { 110, 150, 300, 600, 1200, 1800, 2400, 4800, 9600, 19200, 38400, 57600, 115200 }; static const char* baudrate_char_selected = "not selected"; int baudrate_index_selected; createCombo("Baudrate", baudrates_char, IM_ARRAYSIZE(baudrates_char), baudrate_char_selected, &baudrate_index_selected);

IM_ARRAYSIZE är alltså lika med

IM_ARRAYSIZE(_ARR) = ((int)(sizeof(_ARR) / sizeof(*(_ARR))))

En manual hur man använder ComboBox i ImGui finns här: https://github.com/ocornut/imgui/issues/1658

Frågeställning:
Jag förstår koden och vet att när jag ändrar en const pekar-array i C++ (eller C) så kommer värdet på denna array bara vara tillfällig. Jag vill att den ska verkligen skriva över värdet till baudrate_char_selected. Hur gör man då?

Permalänk
Hedersmedlem

Att man hamnar i den här situationen indikerar oftast att någonting är galet. Kanske är biblioteket man använder inte tänkt för det vad man vill göra, men oftast gör man bara inte på det rekommenderade sättet. Const-variabler är inte avsedda att modifieras; försök undvika att göra det. Oväntade saker kan hända.

Permalänk
Datavetare

Skulle vara spännande att höra vad Bjarne har att sägas om den där C++ givet diskussionen i den här tråden.

Angående detta: givet att du döpt din funktion till "createCombo()" tror jag du kanske missförstått vad funktionen gör.
Den skapar egentligen inte en comboBox, den renderar en comboBox (anropas varje "frame").

Antar att målet är att kunna välja baud-rate ur listan, det kan skrivas t.ex. så här

int renderComboBox(std::string label, const std::vector<std::string> &items, int selected_index) { if (ImGui::BeginCombo(label.c_str(), items[selected_index].c_str())) { for (int i = 0; i < items.size(); i++) { bool is_selected = (i == selected_index); if (ImGui::Selectable(items[i].c_str(), is_selected)) selected_index = i; if (is_selected) ImGui::SetItemDefaultFocus(); } ImGui::EndCombo(); } return selected_index; } ... main-f*cking-game-loop ... ImGui::Begin("something"); ... static int selected_baudrate_index = 0; // don't do this, it should be part of some game-state somewhere static std::vector<std::string> baudrates{ "110", "150", "300", "600", "1200", "1800", "2400", "4800", "9600", "19200", "38400", "57600", "115200" }; selected_baudrate_index = renderComboBox("Baudrate", baudrates, selected_baudrate_index); ... ImGui::End(); ...

renderComboBox() ovan kan användas till att visa vad som helst bara man gör det till en std::vector<std::string>

tab/space...
Visa signatur

Care About Your Craft: Why spend your life developing software unless you care about doing it well? - The Pragmatic Programmer

Permalänk

Okej. Du använder alltså std::string för att lösa problemet. Jag är så van med C programmering så char* existerar mest bara för mig

Angående vad Bjarne skulle säga så skulle han säga att han vill implementera en analyzerare för C++ standarden.

Permalänk
Datavetare
Skrivet av heretic16:

Okej. Du använder alltså std::string för att lösa problemet. Jag är så van med C programmering så char* existerar mest bara för mig

Angående vad Bjarne skulle säga så skulle han säga att han vill implementera en analyzerare för C++ standarden.

Well, i första hand säger han att man ska låta bli att programmera pre-2011 style C++ då det hänt en hel del positiva saker i språket sedan dess.

Men just i det här fallet ska man väl främst gälla på ImGui, deras exempel är typ 80/90-tals C++...

Om du skulle vilja fixa din egen lösning ligger problemet här

selected_item = const_cast<char*>(items[i]); *selected_index = i;

för att kunna få tillbaka något behöver du en pekare/referens till där man vill lagra "selected_item"

T.ex. detta

void createCombo(const char* label, const char* items[], int length_items, const char** selected_item, int* selected_index) { if (ImGui::BeginCombo(label, selected_item)) { for (int i = 0; i < length_items; i++) { bool is_selected = (selected_item == items[i]); // You can store your selection however you want, outside or inside your objects if (ImGui::Selectable(items[i], is_selected)) { *selected_item = const_cast<char*>(items[i]); *selected_index = i; std::cout << selected_item << std::endl; } } ImGui::EndCombo(); } }

Rätt gott om sätt att skjuta sig både i huvudet och fötterna, men _borde_ fungera (har inte testat)

Visa signatur

Care About Your Craft: Why spend your life developing software unless you care about doing it well? - The Pragmatic Programmer

Permalänk
Skrivet av Yoshman:

Well, i första hand säger han att man ska låta bli att programmera pre-2011 style C++ då det hänt en hel del positiva saker i språket sedan dess.

Men just i det här fallet ska man väl främst gälla på ImGui, deras exempel är typ 80/90-tals C++...

Om du skulle vilja fixa din egen lösning ligger problemet här

selected_item = const_cast<char*>(items[i]); *selected_index = i;

för att kunna få tillbaka något behöver du en pekare/referens till där man vill lagra "selected_item"

T.ex. detta

void createCombo(const char* label, const char* items[], int length_items, const char** selected_item, int* selected_index) { if (ImGui::BeginCombo(label, selected_item)) { for (int i = 0; i < length_items; i++) { bool is_selected = (selected_item == items[i]); // You can store your selection however you want, outside or inside your objects if (ImGui::Selectable(items[i], is_selected)) { *selected_item = const_cast<char*>(items[i]); *selected_index = i; std::cout << selected_item << std::endl; } } ImGui::EndCombo(); } }

Rätt gott om sätt att skjuta sig både i huvudet och fötterna, men _borde_ fungera (har inte testat)

Jag har hört talas som Modern C++. Vad detta innebär vet jag dock inte. Jag har dock hört att man ska undvika pekare och använda smarta pekare istället. Dessa ska tydligen vara minnessäkra. Men oavsett vad Dr Bjarne gör så bör C++ överväga att ställa krav på säkerhet. Titta lite mera hur Rust gör. Rust har väll en analyserare som analyserar koden och avgör om det finns minnesluckor. Varför inte implementera sådant i C++.

Ja, ImGui är 80/90-tals C++. Dels av effektiviseringen.

Det spelar ingen roll om jag följer samma kod som i GitHub-länken eller min kod. Resultatet blir det samma.
Men jag ska börja använda lite mer vector. Jag är dåligt på använda detta. Jag programmerar mest bara C med C++ bibliotek.

Permalänk
Skrivet av heretic16:

Frågeställning:
Jag förstår koden och vet att när jag ändrar en const pekar-array i C++ (eller C) så kommer värdet på denna array bara vara tillfällig. Jag vill att den ska verkligen skriva över värdet till baudrate_char_selected. Hur gör man då?

Skrivet av heretic16:

Okej. Du använder alltså std::string för att lösa problemet.

Ja och nej, han löser problemet med hjälp av string, men han har valt att skriva det i en mordernare stil och då blev det string istället. Han har inte löst ditt ursprungliga problem genom att använda string.

Ditt ursprunliga problem handlar om att du sätter om den lokala variabeln i createCombo och förväntar dig att värdet skall ändras i den anropande funktionen. Det har ingenting med const att göra. Om du skrivit

void bar(int x) { x = 17; } void foo() { int a = 4711; bar(a); }

Hade du förväntat dig att a ändrat värde?

Permalänk
Medlem

Så för att summera denna tråd:
Om du skickar in en pekare till en funktion och ändrar den, så får detta ingen effekt alls utanför funktionen. Du vill alltså istället skicka in en pekare TILL en pekare.

Detta har absolut ingenting att göra med att C++ inte är "minnessäkert" eller att kompilatorn inte "analyserar minnesluckor" (på vilket sätt?).