2013-04-03 2 views
1

Редактировать: поскольку параметр attack.condition будет иметь несколько значений, инструкция switch не будет работать!Есть ли альтернатива этому длинному списку «if argA == argB do ...»?

Так что у меня это перечисление , который будет расти:

enum Condition { Null   = 0x0001, 
      SelfIsUnderground = 0x0002, 
      SelfIsGround  = 0x0004, 
      SelfIsAir  = 0x0008, 
      SelfIsWater  = 0x0010, 
      OtherIsUnderground = 0x0020, 
      OtherIsGround  = 0x0040, 
      OtherIsAir  = 0x0080, 
      OtherIsWater  = 0x0100, 
      Tile   = 0x0200, 
      CONDITION_COUNTX = 0x03FF}; 

и эта функция, которая также будет расти:

bool Attack::CanBeDone(Spirit* pSelf, Spirit* pTarget,Map* pMap) 
{ 
    if(this->condition!=Null) 
    { 
     if(this->condition & SelfIsUnderground) 
      if(pSelf->GetcurrentLayer()!=Underground) 
       return false; 

     if(this->condition & SelfIsGround) 
      if(pSelf->GetcurrentLayer()!=Ground) 
       return false; 

     if(this->condition & SelfIsAir) 
      if(pSelf->GetcurrentLayer()!=Air) 
       return false; 

     if(this->condition & SelfIsWater) 
      if(pSelf->GetcurrentLayer()!=Water) 
       return false; 

     if(this->condition & OtherIsUnderground) 
      if(pTarget->GetcurrentLayer()!=Underground) 
       return false; 

     if(this->condition & OtherIsGround) 
      if(pTarget->GetcurrentLayer()!=Ground) 
       return false; 

     ... 

Есть ли альтернатива писать снова и более поздняя:

if(this->condition & arg) 
     if(pSelf->GetcurrentLayer()!=value) 
      return false; 

?

Бонус: Будет ли он работать, если я дам Condition :: Null значение 0x0000, SelfIsUnderground 0x0001, SelfIsGround 0x0002 и снова с полномочиями 2? В конце концов, Tile получит значение 0x0100.

+0

Что я могу купить с моими бонусами? – chris

+0

Я не упоминал слово «точки»: p –

+0

Не можете ли вы пройти через значения «enum»? Смотрите это: http://stackoverflow.com/questions/261963/c-iterate-through-an-enum –

ответ

2

Во-первых, я хотел бы написать что-то вроде этого:

enum Condition { Null   = 0x0001, 
      SelfBase  = 0x0002, 
      SelfIsUnderground = SelfBase * (1 << Underground), 
      SelfIsGround  = SelfBase * (1 << Ground), 
      SelfIsAir  = SelfBase * (1 << Air), 
      SelfIsWater  = SelfBase * (1 << Water), 
      SelfMask  = SelfBase * ((1 << Max) - 1), 
      OtherBase  = 0x0020, 
      OtherIsUnderground = OtherBase * (1 << Underground), 
      OtherIsGround  = OtherBase * (1 << Ground), 
      OtherIsAir  = OtherBase * (1 << Air), 
      OtherIsWater  = OtherBase * (1 << Water), 
      OtherMask  = OtherBase * ((1 << Max) - 1), 
      Tile   = 0x0200, 
      CONDITION_COUNTX = 0x03FF}; 

(в предположении, что Underground == 0 и Max == Water + 1). Затем длинный список сводится к двум довольно четким выражениям:

if ((SelfMask & this->condition & (SelfBase * (1 << pSelf->GetcurrentLayer())))) 
    return false; 

if ((OtherMask & this->condition & (OtherBase * (1 << pTarget->GetcurrentLayer())))) 
    return false; 

return true; 

Это остается верным при расширении перечислений. Обратите внимание, однако, что все еще существует избыточность (например, как определены OtherBase и Tile), что также может быть уменьшено. Статические утверждения могут помочь удостовериться, что перечисление Condition четко определено.

+1

'N * (1 << X)' совпадает с 'N << X'. –

+0

@BenVoigt: мне легче понять мою версию, чем, скажем, 'SelfBase << Underground'. Оставьте некоторые работы для оптимизатора :-) – krlmlr

+0

Мне нравится, что вы сделали. Уже поздно, и я пойду спать, но я посмотрю на это подробно. Благодарю. –

0

Если связать Layer и Condition перечислений вместе, вы можете написать что-то на линии:

enum Condition { Null   = 0x0001, 
    SelfIsUnderground = 0x0002, 
    SelfIsGround  = 0x0004, 
    SelfIsAir  = 0x0008, 
    SelfIsWater  = 0x0010, 
    OtherIsUnderground = 0x0020, 
    OtherIsGround  = 0x0040, 
    OtherIsAir  = 0x0080, 
    OtherIsWater  = 0x0100, 
    Tile   = 0x0200, 
    CONDITION_COUNTX = 0x03FF}; 

enum Layer 
{ 
    Ground = SelfIsGround, 
    Underground = SelfIsUnderground, 
    Air = SelfIsAir, 
    Water = SelfIsWater 
}; 

struct Spirit 
{ 
    Layer GetcurrentLayer() const; 

}; 

struct Map; 

struct Attack 
{ 
    Condition condition; 
    bool CanBeDone(Spirit* pSelf, Spirit* pTarget,Map* pMap) 
    { 
     return (condition & pSelf->GetcurrentLayer())&& 
      ((condition/0x10) & pTarget->GetcurrentLayer()); 
    } 

}; 

Скорее, я бы ожидать, определить Condition с точки зрения Layer, но это только детали ,

+1

Можете ли вы выразить жестко закодированные «0x10» в терминах значений, определенных в перечислении? В противном случае вам придется не забудьте обновить этот код при увеличении перечисления. – krlmlr

+0

@krlmr. Ну, конечно, в реальном коде вы инкапсулировали бы это, как хотите. – Keith

+0

@krlmr. Просто прочитайте свой ответ. Я больше ничего не говорю. – Keith

0

Это то, что вы получаете для преждевременной оптимизации, используя битмаски и побитовые операции; нет простой альтернативы.

Почему не std::vector<Layer> selfLayers, otherLayers? (Или вы не можете иметь несколько слоев - ваш код подразумевает, что вы можете.)
Или, может быть, использовать карту от слоя к requiredConditionBits?

Кроме того, я не понимаю, почему класс Attack сохраняет условия для двух Spirit s. Кто устанавливает Attack::condition в первую очередь?

Я бы начал с того, что не использовал битовые карты для вещей, которые не являются битами или перечислениями для вещей, которые не являются перечислениями. Я бы потратил некоторое время на создание простых классов (например, Layer, Condition) и вспомогательные функции (Attack::getSourceLayer, может быть, я слишком смущен, чтобы быть уверенным.) Я также рассмотрел бы мои отношения классов, чтобы убедиться, что Attack действительно нужен condition flags et так далее.

+0

Слои играют гораздо большую роль, чем то, что показано в моем вопросе. По этой причине я не могу делать то, что вы говорите. –

0

Я бы сказал, что полностью отказаться от перечисленияи использовать вместо него структуру.Условия для себя и цели являются отдельными и должны рассматриваться как таковые

enum Layer : char //only takes 1 byte 
{ 
    Ground  = 1<<0, 
    Underground = 1<<1, 
    Air   = 1<<2, 
    Water  = 1<<3 
}; 
struct Condition { 
    Layer Self; 
    Layer Target; 
}; 
bool Attack::CanBeDone(Spirit* pSelf, Spirit* pTarget,Map* pMap) { 
    return this->Condition.Self & pSelf->GetcurrentLayer() 
     && this->Condition.Target & pTarget->GetcurrentLayer(); 
} 
Смежные вопросы