有没有办法在 C++ 中重定向字符串输出

问题描述 投票:0回答:0

我目前正在做一个 C++ 项目,我有一些函数可以使用 std::cout 将一些信息输出到控制台。我还有一个测试函数调用这些函数来检查它们是否正常工作。但是,我发现这些函数的输出在运行测试函数时会让人分心,我想知道是否有办法将这些函数的输出重定向到一个文件或完全抑制它。

我尝试使用

rdbuff
,但不幸的是,这也重定向了我的测试函数的输出。

有没有办法在不修改它们的情况下重定向或抑制这些函数的输出?我正在寻找可以在 C++11 或更高版本中实现的解决方案。任何帮助将不胜感激。

最终我想要打印出来的是:

Test1 PASSED

TEST2 PASSED

TEST3 FAILED

等等……

这是我的代码:

/**
 * @author Jakob B
 * @file main.cpp [Driver Code]
 * @note Driver code for lab4
 * 
 * @brief This assigment focuses on using basic operations in a heap such as:
 *   - Insertion
 *   - Deletion
 *   - Peeking
 * 
 * Those operations were implemented using a Shelter class that includes a struct for Pets 
 */

#include <vector>
#include <iostream>
#include <string>
#include <stdexcept>
#include <limits>
#include <utility>
#include <sstream>
#include <unistd.h>

#pragma message("[" __FILE__"] " "Last compiled on [" __DATE__ "] at [" __TIME__"]")

/**
 * @struct Pet
 *    - Defines the Pet object
 *    - Includes a constructor
 */
struct Pet {
    std::string name;
    int arrival;
    Pet(std::string name = "", int arrival = 0) : name(name), arrival(arrival) {} //Constructor
};

int const PAUSE = 1000000; //usleep const

/**
 * @class PetHeap
 *    - Defines the heap with appropriate methods
 */
class PetHeap {
  private:
    std::vector<Pet*> petHeap;
    void percolateUp(int index);
    void percolateDown(int index);
    void deleteMin();
    Pet* peekMin();

  public:
    PetHeap();
    bool destroyHeap();
    void insert(Pet* pet);
    void adoptOldestPet();
    int numPets();
    void displayPets();
    std::vector<Pet*> massInsertion();

    bool testDestroyHeap();
    bool testInsert(); 
    bool testAdoptOldestPet();
    bool testNumPets(); 
    bool testDisplayPets(); 
    bool testPercolateUp();
    bool testPercolateDown();
    bool testDeleteMin(); 
    bool testPeekMin();

    void runTests();
};

/** CONSTRUCTOR:
 * @name PetHeap::PetHeap()
 * @brief Initializes an empty vector for the Pet pointers
 */
PetHeap::PetHeap() {
  petHeap = std::vector<Pet*> ();
}

/** DESTRUCTOR:
 * @name PetHeap::~PetHeap()
 * @brief Handles memory
 */
bool PetHeap::destroyHeap() {
  while(!petHeap.empty()){
    Pet* value = petHeap[(int)petHeap.size()-1]; //Pet*
    petHeap.pop_back(); //resize
    delete value;
  }
  if(petHeap.empty()) {
    return true;
  }
  return false;
}

/**
 * @name void PetHeap::percolateUp()
 * @brief Percolates up the element at the given index until the heap property is satisfied.
 * @param index The index of the element to be percolated up.
*/
void PetHeap::percolateUp(int index) {
  /**
   * Parent formula: (i-1)/2
   * 
   * Find parent and compare it to the array
  */
 int parent = (index-1)/2;
 if(index == 0 || petHeap[index] > petHeap[parent]) {
  return;
 }  
  //swap the values
  std::swap(petHeap[index], petHeap[parent]);
  percolateUp(parent);
}

/**
 * @name void PetHeap::insert()
 * @brief Inserts a new pet into the heap and maintains the heap property
 * @param pet The pet to be inserted
 */
void PetHeap::insert(Pet* pet) {
  /**
   * Insert into the array
   * Call percolateUp to maintain the heap
  */
 petHeap.push_back(pet);
 int index = petHeap.size() - 1; //track the index of the pet
 percolateUp((int)index);
}


/**
 * @name void PetHeap::percolateDown()
 * @brief Percolates down the pet heap from the given index to maintain the heap property
 * @param index The index from where to start percolating down
 */
void PetHeap::percolateDown(int index) {
  int leftChild = 2*index + 1;
  int rightChild = 2*index + 2;
  int largestNode = index;

  /** @note bad values*/
  if(index < 0 || index > petHeap.size()-1) {
    return;
  }

  /** @note no children and it's a leaf node*/
  if(leftChild > petHeap.size()-1 && rightChild > petHeap.size()-1) {
    return;
  }

  /** @note node has a left child and the value of the current node is greater*/
  if(rightChild >= petHeap.size()-1 && petHeap[index] >= petHeap[leftChild]) {
    return;
  }

  /** @note node has two children and the value of the current node is greater than both children*/
  if(petHeap[index] >= petHeap[leftChild] && petHeap[index] >= petHeap[rightChild]) {
    return;
  }

  /** @note recursive case*/
  if(petHeap[leftChild] > petHeap[index]) {
    largestNode = leftChild;
  }
  if(rightChild < petHeap.size()-1 && petHeap[rightChild] > petHeap[largestNode]) {
    largestNode = rightChild;
  }
  if(largestNode != index) {
    std::swap(petHeap[index], petHeap[largestNode]);
    percolateDown(largestNode);
  }
}

/**
 * @name void PetHeap::deleteMin()
 * @brief Deletes the minimum element from the heap.
 * If the heap is empty, does nothing. Otherwise, deletes the root element (minimum element)
 * from the heap and reorders the heap using percolateDown() to maintain the heap property.
 */
void PetHeap::deleteMin() {
  if(petHeap[0] == nullptr) {
    return;
  }
  int lastElementIndex = petHeap.size() - 1;
  Pet* lastElement = petHeap[lastElementIndex];
  std::swap(petHeap[0], petHeap[lastElementIndex]);
  //now the last element is petHeap[0] and the first element is petHeap[lastElementIndex]
  delete petHeap[lastElementIndex];
  petHeap.resize(petHeap.size()-1);
  percolateDown(0); //sort the heap using percolateDown()
}

/**
 * @name Pet* PetHeap::peekMin()
 * @brief Returns the minimum value of the heap without modifying the heap.
 * @return The minimum Pet object in the heap.
*/
Pet* PetHeap::peekMin() {
  Pet* firstElement = petHeap[0]; //first element
  return firstElement;
}

/**
 * @name void PetHeap::adoptOldestPet()
 * @brief Adopt the oldest pet in the shelter.
 * @return void
*/
void PetHeap::adoptOldestPet() {
  if(petHeap.empty()) {
    std::cout << "\n[Error: No Pets in the shelter]\n";
    return;
  }

  Pet* oldestPet = peekMin();
  std::cout << "\n[Pet adopted]:\n\n";
  std::cout << "[name]: " << oldestPet -> name << std::endl; 
  std::cout << "[arrival]: " << oldestPet -> arrival << std::endl;
  std::cout << std::flush;
  deleteMin();
  return;
}

/**
 * @name int PetHeap::numPets()
 * @brief Returns the number of pets in the PetHeap.
 * @return The number of pets in the PetHeap as an integer.
*/
int PetHeap::numPets() {
  std::cout << "\n[Number of Pets]: " << petHeap.size() << "\n\n";
  return petHeap.size();
}

/**
 * @name void PetHeap::displayPets() 
 * @brief Display all pets in the shelter
 */
void PetHeap::displayPets() {
  std::cout << "\n[Displaying Heap]:\n\n";
  if(petHeap.size() == 0) {
    std::cout << "[Heap is empty]\n";
  }
  for(Pet* iter : petHeap) {
    std::cout << "[name]: " << iter -> name << std::flush; //flush buffer
    std::cout << "\t[arrival]: "<< iter -> arrival << std::endl;
  }
  return;
}

/** @name getName()
 * @brief Prompts the user to enter a name
 * @param name passed by reference
 * @return std::string name
 */
std::string getName(std::string &name) {
  std::cout << "[Enter Name]: ";
  while (true) {
    try {
      std::cin >> name;
      if (std::cin.fail()) {
        throw std::runtime_error("[Invalid input. Please enter a valid name]");
      }
      return name;
    } catch (const std::runtime_error& e) {
      std::cerr << "Error: " << e.what() << std::endl;
      std::cin.clear();
      std::cin.ignore(10000, '\n');
    }
  }
}

/** @name getArrival()
 * @brief Prompts the user to enter the arrival time
 * @param arrival passed by reference
 * @return (int) arrival
 */
int getArrival(int &arrival) {
  std::cout << "\n[Enter Arrival]: ";
  while (true) {
    try {
      std::cin >> arrival;
      if (std::cin.fail() || arrival < 0) {
        throw std::runtime_error("[Invalid input. Please enter a positive integer]");
      }
      return arrival;
    } catch (const std::runtime_error& e) {
      std::cerr << "Error: " << e.what() << std::endl;
      std::cin.clear();
      std::cin.ignore(10000, '\n');
      std::cout << "\n[Enter Arrival]: ";
    }
  }
}

/**
 * @name int getInput()
 * @brief gets the input for the switch statement
 * @return (int) input
 */
int getInput() {
  int const MIN = 1;
  int const MAX = 7;
  int choice = 0;
  std::cout << "\n[Enter]: ";
  
  while (true) {
    try {
      std::cin >> choice;
      if (std::cin.fail()) { //std::cin.fail() if the input is not an intiger returns true
        /// @link https://cplusplus.com/forum/beginner/2957/
        
        std::cin.clear(); // clear error flags
        std::cin.ignore(10000, '\n'); // ignore up to 10000 characters or until a newline is encountered
        throw std::invalid_argument("[Invalid input]");
      }
      else if (choice < MIN || choice > MAX) {
        throw std::out_of_range("[Input out of range. Please enter an integer between 1 and 16]");
      }
      else {
        return choice;
      }
    }
    catch (const std::exception& error) {
      std::cout << error.what() << std::endl;
      std::cout << "[Re-enter]: ";
    }
  }
}

/** @name goodbye()
 * @brief The function prompts the user goodbye
 * @remark Handles UI
 * @return void-type
 */
void goodbye() {
  std::cout << "\nGoodbye!\n\n";
}

/**
 * @brief Destroys the heap and prints a message to the console
 * @param heap A reference to the `PetHeap` object to be destroyed.
 */
void isDestroyed(PetHeap &heap) {
  bool dead = false;
  bool retry = false;
  dead = heap.destroyHeap();
  if(dead) {
    std::cout << "\n[Heap Destroyed]\n";
    goodbye();
  } else {
    std::cout << "\n[Something went wrong]\n";
    retry = heap.destroyHeap(); //try again
      if(retry == false) {
        std::cout << "\n[Program is leaking memory!]\n";
        std::cout << "\n[Force Exiting with code 1]\n";
        std::exit(EXIT_FAILURE);
      }

  }
}

/**
 * @name void menu()
 * @brief Prints the menu for the user
 */
void menu() {
  std::cout << "\n\n"
            << "[1]   Insert a Pet\n"
            << "[2]   Adopt a Pet\n"
            << "[3]   Number of Pets in the Heap\n"
            << "[4]   Display Heap\n"
            << "[5]   Run Tests\n"
            << "[6]   Exit\n";
  return;
}

/** @name printTitle()
 * @brief The function prints the title
 * @remark Handles UI
 * @return void-type
 */
void printTitle() {
  std::cout << "\n\n*** Welcome to the Heap assigment *** \n\n";
  std::cout << "Please select one of the following?\n";
}


/**
 * 
*/
std::vector<Pet*> PetHeap::massInsertion() {
  PetHeap heap;
  std::vector<std::string> namesInput = {"F", "G", "E", "A", "B", "K", "L", "Q", "T", "Y", "O", "M"}; //12
  std::vector<int> arrivalInput = {2, 6, 12, 3, 7, 18, 36, 6, 9, 11, 10, 29}; //12
  
  std::vector<Pet*> inputValues; //vector with Pet pointers
  inputValues.reserve(namesInput.size());

  for(int i = 0; i < namesInput.size(); i++) {
    inputValues.emplace_back(new Pet(namesInput[i], arrivalInput[i]));
  }
  
  return inputValues;
}

/**
 * 
*/
bool PetHeap::testDestroyHeap() {
  //test destroying a heap with pets
  std::vector<Pet*> pets = massInsertion();
  bool done = destroyHeap();

  std::cout << "TEST1 FINISHED\n" << std::flush;
  usleep(PAUSE);
  return done && petHeap.empty();
}

bool PetHeap::testInsert() {
  //inserting a single pet
  Pet* pet1 = new Pet("Fido", 3);
  insert(pet1);
  bool result1 = peekMin() == pet1 && petHeap.size() == 1;

  //inserting multiple pets
  std::vector<std::pair<std::string, int>> newPets = {{"Rufus", 6}, {"Buddy", 2}};
  for (auto pet : newPets) {
    Pet* newPet = new Pet(pet.first, pet.second);
    insert(newPet);
  }
  bool result2 = peekMin()->name == "Buddy" && petHeap.size() == 3;
  std::cout << "TEST2 FINISHED\n" << std::flush;
  usleep(PAUSE);
  return result1 && result2;
}

bool PetHeap::testAdoptOldestPet() {
  //adopting the oldest pet from a heap with pets
  std::vector<Pet*> pets = massInsertion();
  Pet* oldestPet = pets[0];
  bool result1 = false;
  if (!pets.empty()) {
    result1 = destroyHeap() && peekMin() != oldestPet && petHeap.size() == pets.size() - 1;
  }

  //adopting the oldest pet from an empty heap
  destroyHeap();
  adoptOldestPet();
  bool result2 = petHeap.empty();

  std::cout << "TEST3 FINISHED\n" << std::flush;
  usleep(PAUSE);
  return result1 && result2;
}

bool PetHeap::testNumPets() {
  //getting the number of pets in a heap with pets
  std::vector<Pet*> pets = massInsertion();
  bool result1 = false;
  if (!pets.empty()) {
    for (auto pet : pets) {
      insert(pet);
    }
    result1 = numPets() == pets.size();
  }

  //getting the number of pets in an empty heap
  destroyHeap();
  bool result2 = numPets() == 0;

  std::cout << "TEST4 FINISHED\n" << std::flush;
  usleep(PAUSE);
  return result1 && result2;
}

bool PetHeap::testDisplayPets() {
  //displaying the pets in a heap with pets
  std::vector<Pet*> pets = massInsertion();
  bool result1 = false;
  std::ostringstream oss; //declare oss block
  if (!pets.empty()) {
    for (auto pet : pets) {
      insert(pet);
    }
    displayPets();
    //fix oss
    result1 = oss.str() == "A(2) B(7) E(12) F(2) G(6) K(18) L(36) M(29) O(10) Q(6) T(9) Y(11) ";
  }

  //displaying the pets in an empty heap
  oss.str("");
  destroyHeap();
  displayPets();
  bool result2 = oss.str() == "";

  std::cout << "TEST5 FINISHED\n" << std::flush;
  usleep(PAUSE);
  return result1 && result2;
}

bool PetHeap::testPercolateUp() {
  std::vector<Pet*> inputValues = massInsertion();
  int index = inputValues.size() / 2;
  int parentIndex = (index - 1) / 2;

  //percolating up an element
  Pet* pet = new Pet("Fido", 5);
  petHeap[index] = pet;
  percolateUp(index);
  bool result1 = petHeap[parentIndex] == pet;

  //percolating up an element that's already at the root
  percolateUp(0);
  bool result2 = true;

  std::cout << "TEST6 FINISHED\n" << std::flush;
  usleep(PAUSE);
  return result1 && result2;
}

bool PetHeap::testPercolateDown() {
  std::vector<Pet*> inputValues = massInsertion();
  int index = 0;
  int leftChildIndex = 2 * index + 1;
  int rightChildIndex = 2 * index + 2;

  //percolating down an element
  Pet* pet = new Pet("Fido", 5);
  petHeap[index] = pet;
  percolateDown(index);
  bool result1 = petHeap[leftChildIndex] == pet;

  //percolating down an element that's already a leaf
  petHeap[leftChildIndex] = petHeap.back();
  petHeap.pop_back();
  percolateDown(leftChildIndex);
  bool result2 = true;

  std::cout << "TEST7 FINISHED\n" << std::flush;
  usleep(PAUSE);
  return result1 && result2;
}

bool PetHeap::testDeleteMin() {
  std::vector<Pet*> inputValues = massInsertion();

  //deleting the minimum element
  Pet* minPet = peekMin();
  deleteMin();
  bool result1 = peekMin() != minPet && petHeap.size() == inputValues.size() - 1;

  //deleting the minimum element from an empty heap
  destroyHeap();
  deleteMin();
  bool result2 = petHeap.empty();

  std::cout << "TEST8 FINISHED\n" << std::flush;
  usleep(PAUSE);
  return result1 && result2;
}

bool PetHeap::testPeekMin() {
  std::vector<Pet*> inputValues = massInsertion();

  //peeking at the minimum element
  Pet* minPet = peekMin();
  bool result1 = minPet != nullptr && minPet->arrival == 2;

  //peeking at the minimum element of an empty heap
  destroyHeap();
  Pet* emptyMinPet = peekMin();
  bool result2 = emptyMinPet == nullptr;

  std::cout << "TEST9 FINISHED\n" << std::flush;
  usleep(PAUSE);
  return result1 && result2;
}

/**
 * @name void runTests()
 * @brief Runs tests for all the functions
 */
void PetHeap::runTests() {
  std::vector<bool> testValues;

  std::cout << "\n*** RUNNING ALL TESTS ***\n\n" << std::flush;
  usleep(PAUSE);

  testValues.push_back(testDestroyHeap());
  testValues.push_back(testInsert()); 
  testValues.push_back(testAdoptOldestPet());
  testValues.push_back(testNumPets()); 
  testValues.push_back(testDisplayPets()); 
  testValues.push_back(testPercolateUp());
  testValues.push_back(testPercolateDown());
  testValues.push_back(testDeleteMin()); 
  testValues.push_back(testPeekMin());

  for(auto i = 0; i < testValues.size(); i++) {
    if(testValues[i] == true) {
      std::cout << "TEST " << i+1 << " PASSED ***";
    }else if(testValues[i] == false) {
      std::cout << "TEST " << i+1 << " FAILED ###";
    }
  }
}
/**
 * @brief Main function of the program
 * @return EXIT_SUCESS upon succesful execution
 */
int main() {
  PetHeap heap;
  std::string name = "";
  int arrival = 0;

  enum OP_ID {
    ID_1 = 1,
    ID_2,
    ID_3,
    ID_4,
    ID_5,
    ID_6,
  };
  printTitle();
  while(true) {
    menu();
      switch(getInput()) {
      case OP_ID::ID_1: {
        heap.insert(new Pet(getName(name), getArrival(arrival)));
        break;
      }
        
      case OP_ID::ID_2: {
        heap.adoptOldestPet();
        break;
      }
        
      case OP_ID::ID_3: {
        heap.numPets();
        break;
      }
        
      case OP_ID::ID_4: {
        heap.displayPets();
        break;
      }
        
      case OP_ID::ID_5: {
        heap.runTests();
        break;
      }
        
      case OP_ID::ID_6: {
        isDestroyed(heap);
        exit(EXIT_SUCCESS);
        break;
      }
        
      default: {
        /** @note do nothing*/
      }
      }
  }
  return EXIT_SUCCESS; 
}

代码有一些错误,并且缺少一些文档,因为我还没有完成实验。我希望这不会出现和发布,但如果出现,我非常抱歉。

c++ c++11 ostream
© www.soinside.com 2019 - 2024. All rights reserved.