2011-01-14 24 views
5

¿Cómo se pueden crear menús en el programa de línea de comandos? He intentado cosas como:crear menús en la línea de comando

cin >> input; 
switch (input) { 
    case (1): 
    // do stuff 
    case (2): 
    // ... 
} 

pero entonces he tenido el problema de sub-menús, y volviendo al mismo menú, etc. El primer programa que escribí (aparte de los ejercicios) que intentó utilice la idea switch para los menús tenía goto declaraciones porque la alternativa era montones de (en ese momento) bucles complicados.

Respuesta

8

Si traté de contar las formas en que se podría crear un menú de 1, 2, los dos estaríamos muertos antes de iterar 1/2 de ellos. Pero aquí hay un método que podría tratar de que pueda empezar (no probado, es posible que tenga que limpiar un par de cosas):

struct menu_item 
{ 
    virtual ~menu_item() {} 
    virtual std::string item_text() const = 0; 
    virtual void go() = 0; 
}; 

struct print_hello_item 
{ 
    std::string item_text() const { return "display greeting"; } 
    void go() { std::cout << "Hello there, Mr. User."; } 
}; 

struct kill_everyone_item 
{ 
    std::string item_text() const { return "Go on murderous rampage"; } 
    void go() { for(;;) kill_the_world(); } 
}; 

struct menu_menu_item 
{ 
    menu_menu_item(std::string const& text) : text_(text), items() {} 
    void add_item(std::unique_ptr<menu_item> item) { items.push_back(std::move(item)); } 
    void go() 
    { 
    std::cout << "Choose: \n"; 
    std::for_each(items.begin(), items.end(), [](std::unique_ptr<menu_item> const& item) 
    { 
     std::cout << "\t" << item->item_text() << "\n"; 
    }); 
    std::cout << "\n\n\tYour choice: "; 
    int choice = get_number_from_console(); 
    if (items.size() > choice) items[choice]->go(); 
    } 
    std::string item_text() const { return text_; } 

private: 
    std::string text_; 
    std::vector<std::unique_ptr<menu_item> > items; 
}; 

int main() 
{ 
    menu_menu_item top_item; 
    top_item.add(std::unique_ptr<menu_item>(new print_hello_item)); 
    top_item.add(std::unique_ptr<menu_item>(new kill_everyone_item)); 

    top_item.go(); 
} 

Como ejerza, cómo podría yo definir los elementos del menú, así:

top_level.add() 
    ("Drive off a cliff", &die_function) 
    ("Destroy the world", &global_thermal_nuclear_war) 
    ("Deeper", submenu() 
       ("Hey, check this shit out!", &gawk)) 
; 

Se puede hacer con el marco anterior como punto de partida.

Esta es la diferencia entre el diseño OO y lo que podría llamarse "procedural". Creé una abstracción detrás de lo que significa ser una opción de menú (que puede ser otro menú) que se puede extender en varias direcciones. Creo las extensiones que necesito, las junté y digo lo que necesito. Un buen diseño de OO es así ... la parte principal de tu programa consiste en juntar cosas y decirles que se vayan.

La clave para sacar de esto no es necesariamente hacerlo de la manera en que lo hice, sino pensar en ello de una manera diferente. Si puede obtener la esencia del código anterior, verá que puede agregar nuevos elementos, con nuevos menús, a profundidades arbitrarias, sin tener que lidiar con el tipo de código excesivamente complicado que causa el estilo de cambio.

+0

Supongo que esta es una solución preferida, ya que es más flexible, y puede almacenar las cosas de framework-y en otro archivo, y consultar más adelante. –

+0

Sin duda es preferible a cualquier forma de conmutación. Sí, ciertamente podría ampliarlo con algún tipo de lector de formato de archivo para que el menú también se haya creado a partir de una entrada externa. –

+0

std :: struct en kill_everyone_item debe reemplazarse por std :: string – Septagram

2

puede integrar submenús en el menú con su método:

cin >> input; 
switch (input) { 
    case (1): 
    cin >> input; 
    switch (input) { 
     case (1): //do stuff 
     case (2): //do stuff 
    } 
    break; 
    case (2): 
    break; 
} 

Es esto lo que está buscando? De lo contrario: ¿qué quieres resolver exactamente?

Edit: ¿Entonces lo que necesita es un bucle adicional en sus submenús con una condición de corte?

do{ 
    cin >> input; 
    switch (input) { 
     case (1): 
     do{ 
      cin >> input; 
      switch (input) { 
      case (1): //do stuff 
      case (2): //do stuff 
      } 
     }while(input != 3); 
     break; 
     case (2): 
     break; 
    } 
    }while(true); 
+0

En el contexto del menú que brinda, esperaba que una vez que el usuario completara una acción (como el primer elemento de hacer, volvería al submenú, y que habría una opción para volver al primer menú. ¿Eso tiene sentido? –

+0

Ah, está bien - ya veo ... He agregado el código fuente para este caso – Constantin

+0

Parece que funcionaría. –

1

Acabo de hacer una pregunta similar en Libraries for displaying a text-mode menu? - parece que ncurses simplificará drásticamente las partes que muestran el menú.

+1

imho, ncurses no es una gran respuesta, ya que sus menús no son OOP y en su lugar implementan listas enlazadas basadas en punteros '*' y '**'. Estoy en una búsqueda similar para un menú, navegación, control de clase/biblioteca y estoy tratando de no hacer YAIEI o YAPBLL. Sin embargo, otra (if-else-if | lista vinculada basada en puntero) –

+0

Estoy de acuerdo. Traté de usar la funcionalidad del menú Curses, pero renuncié y escribí mi propia versión (estilo C++, uso de STL) en su lugar. Añadiré eso a la pregunta que hice. – ExOttoyuhr

+0

Me rendí en OOP. :(Casi me siento como un fracasado, pero tengo que volver a trabajar realmente y esta aventura de una semana de prueba fue suficiente. Simplemente no puedo juntar las piezas en mi cerebro. Tomé este ejemplo de creación de estructuras desde 1995 y hace lo que quiero. http://dec.bournemouth.ac.uk/staff/awatson/micro/articles/9509_066.pdf La muestra está en mi Github. –

Cuestiones relacionadas