Doimiy katalog yordamida C kodidagi taymer yorlig'i sifatida foydalanish uchun biron bir usul bormi?

Mening teglarim va ularning hash kodlarini belgilaydigan doimiy qiymatlar va qatorlar bor. Misol uchun,

#define LABEL_A 0 //or const int LABEL_A = 0;
#define LABEL_B 1
#define LABEL_C 2
#define LABEL_D 3

const char *VALUE[] = {"LABEL_A", "LABEL_B", "LABEL_C", "LABEL_D"};
const int VALUE_HASH[] = {67490, 67491, 67493, 67459);

At run-time, these labels can come in any order and needs to be parsed accordingly. I am using switch case for this purpose. This code is generating error at compile time "constant expression is required.

function(const char* LabelAtRuntime){
  int i = getHashCode(LabelAtRuntime);
  switch(i){
    case VALUE_HASH[LABEL_A]: //line giving compile time error
      break;
    default:
      break;
}

But, when I provide actual constants, it works. This code works well.

function(const char* LabelAtRuntime){
  int i = getHashCode(LabelAtRuntime);
  switch(i){
    case 67490: //line not giving compile time error
      break;
    default:
      break;
}
  1. Men tushunolmayapman, nega bunday bo'ladi? Ikkala qator va indeks ham sobit bo'lib, unda bu doimiy harflarga teng emasmi?
  2. Sabitlarni kerakli tarzda taqdim etadigan boshqa usul bormi?

Yaxshiroq kod semantikasi, okunabilirlik va qayta foydalanish imkoniyatini ta'minlash uchun men bu kabi uslubda foydalanaman. Iltimos, if-else asosidagi hal qilmang. Yuqoridagi misolda faqat 4 ta teg bor, lekin amalda 100 ta bo'lishi mumkin.

9
Oldindan protsessor matematikasi bilan xash qiymatini hisoblang. Xash funktsiyasi nima?
qo'shib qo'ydi muallif chux, manba
VALUE aslida const emasligini unutmang.
qo'shib qo'ydi muallif immibis, manba
C yoki C ++ ni tanlang. Sizning kodingiz const int LABEL_A kodi bilan kompilyatsiya qilinmaydi, ammo C ++ ning keyingi versiyalarida kompilyatsiya qilinishi mumkin. Shuning uchun "C/C ++" ga javob berishning mazmuni yo'q, iltimos savoldan tilni olib tashlang.
qo'shib qo'ydi muallif Lundin, manba
case 67490://qatorni kompilyatsiya qilish vaqtida xatolikni keltiruvchi .... err..really?
qo'shib qo'ydi muallif Sourav Ghosh, manba
Qanday qilib #define LABEL_A_HASH 67490 haqida?
qo'shib qo'ydi muallif JimmyB, manba
@chux bu java.lang.Stringdan foydalanadigan hash funktsiyasi.
qo'shib qo'ydi muallif Amber Beriwal, manba
- Sourav, Typo hal qilindi. -JimmyB, alohida qadriyatlarni belgilashni istamayman. Yorliqlarni sobit sifatida belgilash maqsadiga yakun yasaydi. -Lundin, men C ga mos keladigan javob izlayapman. Hech narsa topilmasa, men C ++ ga o'tishni o'ylayman. - Shay, bu qanday ishlaydi?
qo'shib qo'ydi muallif Amber Beriwal, manba
Jadvallarni indeksatsiyalash, aslida ko'rsatgichni ajratib olishdir. Men buni derleyici va shuning uchun xatolik orqali statik ravishda bajarish mumkin emasligiga ishonaman.
qo'shib qo'ydi muallif scorpGoku, manba
Funksiyalarga markerlarning bir qatorini yaratishni ko'rib chiqdingizmi va agar kerakli funksiyani if-else so'zlari yoki switch-cases holda chaqirsangiz?
qo'shib qo'ydi muallif Shay Gold, manba

6 javoblar

C ++ da ushbu kompilyatsiya:

#include 
#include 

constexpr int x[] = { 42, 43 };

int main(int argc, char **argv)
{
    switch(atoi(argv[1]))
    {
        case x[0]: puts("forty_two");
                   break;
        case x[1]: puts("forty_three");
    }
    return 0;
}

Shunday qilib, qatordagi constexpr zamonaviy C ++ da hal bo'lishi mumkin. (Eslatma: savol aslida C ++ va C belgilaridan iborat edi)

Agar siz C qatorini saqlamoqchi bo'lsangiz, bu mumkin emas. Vazifalar uchun integer doimiy talab qilinadi, biroq siz tamsayı doimiy o'zgaruvchan bo'lib, u ish vaqti obyekti (u konst deb e'lon qilingan bo'lsa ham) bo'ladi. Siz nima qila olsangiz, in-memory arrayni to'g'ridan-to'g'ri bir guruh bilan belgilashingiz mumkin va ehtimol boshqa so'llarni ishlatib, makrolarni ko'rib chiqadigan makroga ega bo'lishingiz mumkin (kodning shaklini saqlamoqchi bo'lsangiz):

#define LABEL_A 0
#define LABEL_B 1
#define LABEL_C 2
#define LABEL_D 2

#define VALUE_HASH__0 67490
#define VALUE_HASH__2 67491
#define VALUE_HASH__3 67491
#define VALUE_HASH__4 64759

//append what Index expands to to VALUE_HASH__
#define HASH_LOOKUP(Index) MC_cat(VALUE_HASH__,Index) 
#define MC_cat_(X,Y) X##Y
#define MC_cat(X,Y) MC_cat_(X,Y)

function(const char* LabelAtRuntime){
  int i = getHashCode(LabelAtRuntime);
  switch(i){
    case HASH_LOOKUP(LABEL_A)
      break;
    default:
      break;
}
11
qo'shib qo'ydi
@AmberBeriwal Xo'sh, keyin uni C ++ ;-) yorliqli qilmang. Muammoning echimini qo'shdim.
qo'shib qo'ydi muallif PSkocik, manba
Kechirasiz, lekin men C asoslangan echim izlayapman
qo'shib qo'ydi muallif Amber Beriwal, manba

Xatoning sababi shundaki, C const int LABEL_A = 0; ni kompilyatsiya-vaqt sobit deb hisoblamaydi. Afsuski, til qanday aniqlanganligi. Buning o'rniga #define LABEL_A 0 ni ishlatish mumkin.

Uchinchi variant - bu sizning ma'lumotlaringizning barchasini bir-biriga bog'lash va parvarishlash vaqtida ma'lumotlarning bir butunligini ta'minlash uchun ishlatilishi mumkin bo'lgan kamchiliklarni ishlatishdir.

typedef enum
{
  LABEL_A,
  LABEL_B,
  LABEL_C,
  LABEL_D,
  LABELS_N
} label_index_t;

typedef void func_t (void);

typedef struct
{
  const char* str;
  int         hash;
  func_t*     func;
} value_t;

...

const value_t VALUE [] = 
{
  [LABEL_A] = { .str = "LABEL_A", .hash = 67490, .func = a_func },
  [LABEL_B] = { .str = "LABEL_B", .hash = 67491, .func = b_func },
  [LABEL_C] = { .str = "LABEL_C", .hash = 67493, .func = c_func },
  [LABEL_D] = { .str = "LABEL_D", .hash = 67459, .func = d_func },
};

_Static_assert(sizeof VALUE/sizeof *VALUE == LABELS_N,
               "Size of VALUE does not match label_t.");

...

// instead of switch(n):
VALUE[n].func();
5
qo'shib qo'ydi
@AmberBeriwal C ++ belgilangan boshlanuvchilar kabi narsalar bilan kurashishi mumkin. Qadimgi C yoki C ++ bilan maksimal muvofiqligi uchun har bir qatorni {"LABEL_A", 67490, a_func}, sifatida yozishingiz mumkin. _Static_assert ni static_assert -ga o'zgartirish va assert.h ni qo'shish.
qo'shib qo'ydi muallif Lundin, manba
Men buni sinab ko'rishim mumkin ... lekin u mos keladimi yoki c ++ mos keladimi?
qo'shib qo'ydi muallif Amber Beriwal, manba
Bu juda yaxshi yechim, lekin ko'p javoblarni qabul qilish variantlari yo'q: D
qo'shib qo'ydi muallif Amber Beriwal, manba

Kommutatorlik holatida qadriyatlar derleme vaqtida ma'lum bo'lishi kerak. Jadval qiymatlari holatlarida, qiymatlar ish vaqtigacha ma'lum emas.

Agar siz C ++ 11 dan foydalansangiz, kompilyatorni derleme vaqtida qator qiymatlarini baholashga majbur qiladigan constexpr dan foydalanishingiz mumkin. Quyidagi kod yaxshi ishlaydi.

constexpr int VALUE_HASH[] = {67490, 67491, 67493, 67459};

int i = getHashCode(LabelAtRuntime);
switch(i) {
  case VALUE_HASH[LABEL_A]:
    break;
  default:
    break;
}
4
qo'shib qo'ydi
Javobni C ++ deb yozishmadi, shuning uchun constexpr ehtimol variant emas.
qo'shib qo'ydi muallif JeremyP, manba
@JeremyP Afsuski, men xato qilib, ham c, ham c ++ bilan belgilangan. Ehtimol, u o'z qarorini qo'ydi.
qo'shib qo'ydi muallif Amber Beriwal, manba
@Hesham Men yechimni izlayapman
qo'shib qo'ydi muallif Amber Beriwal, manba

Men nima bilan tugashayotganingizni bilmayman. Lekin men C menyusida UI ni amalga oshirishim kerak bo'lganida, shunday qildim:

// Typedef for a menu item's logic function (callback):
typedef void (*menu_item_cb_t)(void*)

// Struct defining a menu item:
typedef struct menu_item {
  const char* label;
  const int hashValue;
  const menu_item_cb_t callback;
  const void* callback_arg;
} menu_item_t;


// Callback for menu item "Do X":
void menu_do_x( void* arg ) {
//...
}

// Definition of menu item "Do X":
const menu_item_t menu_item_x = {
  "Do X",
  12345,
  &menu_do_x,
  NULL//Don't need it to do x
}

// All menu items go into one array:
const menu_item_t* MENU[] = { &menu_item_x, ...};
#define MENU_ITEM_CNT xxx

Keyin quyidagi tanlangan elementda harakat qilishingiz mumkin:

void menuItemSelected( const char* label ) {
  const int hash = getHashCode(label);
  for ( int i = 0; i < MENU_ITEM_CNT; i++ ) {
    const menu_item_t* const mi = MENU[i];
    if ( hash == mi->hash ) {
      mi->callback( mi->callback_arg );
      break;
    }
  }
}

Ushbu yondashuv, albatta, turli xil bo'lishi mumkin, lekin umid qilamanki, bu fikrni qabul qilasiz. Asosan faqat ba'zi xususiyatlarga ega bo'lgan narsalarni belgilash ("teg", "xash" va boshqalar) va ularni ushbu element uchun tegishli amalni bajaradigan funksiya bilan bevosita bog'lash.

2
qo'shib qo'ydi
Ha, albatta. Biz javoblarimizni bir vaqtning o'zida yozishimiz kerak edi :)
qo'shib qo'ydi muallif JimmyB, manba
Bu narsa -Lundinning javobiga juda o'xshaydi. Rahmat. Albatta, yaxshi yechim.
qo'shib qo'ydi muallif Amber Beriwal, manba

Operandlarning barqaror bo'lishi etarli emas. Ular kompilyatsiya vaqtida tan olishning o'zi etarli emas (tbat nima bo'lsa, C standarti bu so'zlarni gapirmaydi). Ish yorlig'i tamsayıli doimiy ifoda bo'lishi kerak.

Tarkibli doimiy ifodalar S standarti bo'yicha aniq belgilangan. Shunga qaramay, integer sobit tamsayı sobit bo'lgan (ham sayımcılar, belgi sobit va shunga o'xshash) bir tamsayı sobit bo'lishi kerak va ular sobit bo'lsa ham, diziler yoki işaretçiler içermemektedir. Mavjud, ammo qo'shimcha tushuntirishlar uchun qarang. bu .

1
qo'shib qo'ydi
Qo'shimchalar switch so'zlarini yozish uchun ixtiro qilingan, shuning uchun siz ularni foydalanishingiz mumkin.
qo'shib qo'ydi muallif n.m., manba
bu sonlar foydalanish mumkin degan ma'noni anglatadimi?
qo'shib qo'ydi muallif Amber Beriwal, manba

Agar VALUE_HASH faqat kalit sobitlarini olish uchun ishlatilsa, nima uchun uni sakrash jadvali bilan almashtirmasligingiz kerak?

Quyidagilardan hech biri testdan o'tkazilmagan yoki hatto tuzilgan. Sintaksis xatolar bo'lishi mumkin.

Birinchidan, jadvaldagi fucntions uchun bir turini belgilang:

typedef void (*Callback)(/* parameters you need */);

Keyin sizning haqiqiy vazifangiz bo'lishi kerak

void labelAProcessing(/* the parameters as per the typedef */)
{
    /// processing for label A
}
// etc

Keyin stoling

Callback valueCallbacks[] = { labelAProcessing, labelBProcessing, ... };

Va sizning kodingiz bo'ladi

int i = getHashCode(LabelAtRuntime);
valueCallbacks[i](/* arguments */);

valueCallbacks qatoriga mos keladigan katalog bo'lishini ta'minlash uchun i kodini tekshirish kerakligini juda ko'p ta'kidlamayman. Aks holda, tasodifiy raqamni funktsiya.

1
qo'shib qo'ydi
Hashcode noto'g'ri nom bo'lishi mumkin. Siz faqat jadval raqami bilan indekslangan jadvalni xohlaysiz. ya'ni hash kodlari 0 da boshlanadi va ketma-ketlikni oshiradi.
qo'shib qo'ydi muallif JeremyP, manba
stol talab qiladigan hajmdan maksimal xash qiymatiga mos kelmasa?
qo'shib qo'ydi muallif Amber Beriwal, manba