기계어 시뮬레이터

Nov 12 2020

SML (Simple Machine Translator)은 16 진수로 작성된 코드를 실행하는 시뮬레이터입니다. 읽기, 쓰기, 더하기, 빼기 등과 같은 기능을 지원합니다. 이 최소 운동 링크에 대한 이전 질문 은 후속 조치를 원하는 사람들을 위해 여기 에서 찾을 수 있습니다 . 나는 많은 변경을 가하고 구조를 변경하고 물건을 옮기고 리뷰를 고맙게 생각합니다.

SML.h

#ifndef SML_SML_H_
#define SML_SML_H_

#include "evaluator.h"

#include <string>

constexpr size_word register_max_size = 6;

enum REGISTERS
{
    ACCUMULATOR = 0, 
    INSTRUCTION_COUNTER = 1,
    TEMPORARY_COUNTER = 2,
    INSTRUCTION_REGISTER = 3,
    OPERATION_CODE = 4,
    OPERAND = 5
};

class SML 
{   
    friend void swap( SML &lhs, SML &rhs );
    friend class Evaluator;

    public:
        SML() = default;
        SML( const int memory_size, const int word_lower_lim, const int word_upper_lim );
        SML( const SML &s );
        const SML& operator=( const SML s );
        SML( SML &&s );
        ~SML();

        void display_welcome_message() const;
        void load_program();
        void execute();
        
    private:
        size_word registers[ register_max_size ];
        std::string temp_str; // holds the string before it is written into the memory
        bool debug;

        static const size_word read_ = 0xA; // Read a word(int) from the keyboard into a specific location in memory
        static const size_word write_ = 0xB; // Write a word(int) from a specific location in memory to the screen
        static const size_word read_str_ = 0xC; // Read a word(string) from the keyboard into a specific location in memory
        static const size_word write_str_ = 0xD; // Write a word(string) from a specific location in memory to the screen
        static const size_word load_ = 0x14; // Load a word from a specific location in memory to the accumulator
        static const size_word store_ = 0x15; // Store a word from the accumulator into a specific location in memory
        static const size_word add_ = 0x1E; /* Add a word from a specific location in memory to the word in the accumulator; store the 
                                        result in the accumulator */
        static const size_word subtract_ = 0x1F;
        static const size_word multiply_ = 0x20;
        static const size_word divide_ = 0x21;
        static const size_word modulo_ = 0x22;
        static const size_word branch_ = 0x28; // Branch to a specific location in the memory
        static const size_word branchneg_ = 0x29; // Branch if accumulator is negative
        static const size_word branchzero_ = 0x2A; // Branch if accumulator is zero
        static const size_word halt_ = 0x2B; // Halt the program when a task is completed
        static const size_word newline_ = 0x32; // Insert a new line
        static const size_word end_ = -0x1869F; // End the program execution
        static const size_word sml_debug_ = 0x2C; // SML debug ( 1 to turn on, 0 to turn off )
        size_word word_lower_limit; /* A word should not exceed */
        size_word word_upper_limit;  /*    this limits */

        size_word memory_size;
        size_word *memory = nullptr;
        
        void set_registers();
        void memory_dump() const;
};

#endif

SML.cpp

#include "sml.h"

#include "evaluator.h"

#include <iostream>
#include <iomanip>
#include <algorithm>

SML::SML( const int mem_size, const int word_lower_lim, const int word_upper_lim ) 
: debug( false ), word_lower_limit( word_lower_lim ),
word_upper_limit( word_upper_lim ), memory_size( mem_size )
{
    set_registers();
    memory = new size_word[ memory_size ];    
}

void SML::set_registers()
{
    registers[ static_cast<unsigned>( ACCUMULATOR ) ] = 0;
    registers[ static_cast<unsigned>( INSTRUCTION_COUNTER ) ] = 0;
    registers[ static_cast<unsigned>( TEMPORARY_COUNTER ) ] = 0;
    registers[ static_cast<unsigned>( INSTRUCTION_REGISTER ) ] = 0;
    registers[ static_cast<unsigned>( OPERATION_CODE ) ] = 0;
    registers[ static_cast<unsigned>( OPERAND ) ] = 0;
}


SML::SML( const SML &s )
{
    temp_str = s.temp_str;
    debug = s.debug;
    word_lower_limit = s.word_lower_limit;
    word_upper_limit = s.word_upper_limit;
    std::copy( std::cbegin( s.registers ), std::cend( s.registers ), registers );
    memory_size = s.memory_size;
    memory = new size_word[ memory_size ];
    std::copy( s.memory, s.memory + s.memory_size, memory );
}

SML::SML( SML &&s )
{
    swap( *this, s );
    memory = new size_word[ memory_size ];
    std::move( s.memory, s.memory + s.memory_size, memory );
}

const SML& SML::operator=( SML s )
{
    swap( *this, s );
    memory = new size_word[ memory_size ];
    std::move( s.memory, s.memory + s.memory_size, memory );

    return *this;
}

void swap( SML &lhs, SML &rhs ) 
{
    using std::swap;

    swap( lhs.temp_str, rhs.temp_str );
    swap( lhs.debug, rhs.debug );
    swap( lhs.word_lower_limit, rhs.word_lower_limit );
    swap( lhs.word_upper_limit, rhs.word_upper_limit );
    swap( lhs.memory_size, rhs.memory_size );
    swap( lhs.registers, rhs.registers );
}

void SML::display_welcome_message() const 
{
    std::cout << "***" <<  " WELCOME TO SIMPLETRON! " << "***\n\n";
    std::cout <<  std::setw( 5 ) << std::left << "***" 
        << "Please enter your program one instruction" 
        << std::setw( 5 ) << std::right << "***\n";

    std::cout << std::setw( 5 ) << std::left  << "***" 
    << "(or data word) at a time. I will type the" 
    << std::setw( 5 ) << std::right << "***\n"; 

    std::cout << std::setw( 5 ) << std::left << "***" 
    << "location number and a question mark (?)."  
    << std::setw( 6 ) << std::right << "***\n";

    std::cout << std::setw( 5 )  << std::left << "***"
    << "You then type the word for that location"  
    << std::setw( 6 ) << std::right  << "***\n";

    std::cout << std::setw( 5 )  << std::left << "***" 
    << "Type the sentinel -0x1869F to stop entering"  
    << std::setw( 5 ) << std::right << "***\n";

    std::cout << std::setw( 5 )  << std::left << "***" 
    << "your program"  
    << std::setw( 5 ) << std::right << "***";

    std::cout << "\n\n" << std::flush;
}

void SML::load_program() 
{
    size_word &ins_cnt = registers[ static_cast<unsigned>( INSTRUCTION_COUNTER ) ];
    size_word temp;
    while( ins_cnt != memory_size ) 
    {
        std::cout << std::setw( 2 ) << std::setfill( '0' )
            << ins_cnt << " ? ";
        std::cin >> std::hex >> temp;
        if( temp == end_ ) {
            break;
        }   
        if( temp >= word_lower_limit && temp < word_upper_limit )
            memory[ ins_cnt++ ] = temp;
        else 
            continue;
    }

    ins_cnt = 0;
    std::cout << std::setfill( ' ' );
    std::cout <<  std::setw( 5 ) << std::left << "***" 
        << "Program loaded into memory" 
        << std::setw( 5 ) << std::right << "***\n";
    
    std::cout <<  std::setw( 5 ) << std::left << "***" 
        << "Program execution starts..." 
        << std::setw( 5 ) << std::right << "***\n";

    execute();
    std::cout << std::endl;
}

void SML::execute() 
{
    int divisor; 
    size_word &ins_cnt = registers[ static_cast<unsigned>( INSTRUCTION_COUNTER ) ];
    size_word &ins_reg = registers[ static_cast<unsigned>( INSTRUCTION_REGISTER ) ];
    
    while( memory[ ins_cnt ] != 0  ) 
    {
        ins_reg = memory[ ins_cnt++ ];

        if( ins_reg < 1000 ) divisor = 0x10; 
        else if( ins_reg >= 1000 && ins_reg < 10000 ) divisor =  0x100; 
        else if( ins_reg >= 10000 && ins_reg < 100000 ) divisor =  0x1000; 

        Evaluator eval( *this ); // create an instance of evaluator
        try
        {
            if( eval.evaluate( *this, ins_reg, divisor ) == 0 )
                break;
        } 
        catch ( std::invalid_argument &e )
        {
            std::cout << e.what() << "\n";
        }
        if( debug )
            memory_dump();
    }
}

void SML::memory_dump() const
{
    std::cout << "\nREGISTERS:\n";

    std::cout << std::setw( 25 ) << std::left << std::setfill( ' ' ) << "accumulator" << std::showpos
        << std::setw( 5 ) << std::setfill( '0' ) << std::internal << registers[ 0 ] << '\n';

    std::cout << std::setw( 28 ) << std::left << std::setfill( ' ' ) 
    << "instruction counter" << std::noshowpos <<  std::setfill( '0' ) 
    << std::right << std::setw( 2 ) << registers[ 1 ] << '\n';

    std::cout << std::setw( 25 ) << std::left << std::setfill( ' ' ) 
    << "instruction register" << std::showpos << std::setw( 5 ) << std::setfill( '0' ) 
    << std::internal << registers[ 3 ] << '\n';

    std::cout << std::setw( 28 ) << std::left << std::setfill( ' ' ) 
    << "operation code" << std::noshowpos <<  std::setfill( '0' ) 
     << std::right << std::setw( 2 ) << registers[ 4 ] << '\n';

    std::cout << std::setw( 28 ) << std::left << std::setfill( ' ' ) 
    << "operand" << std::noshowpos <<  std::setfill( '0' ) 
     << std::right << std::setw( 2 ) << registers[ 5 ] << '\n';

    std::cout << "\n\nMEMORY:\n";
    std::cout << "  ";

    for( int i = 0; i != 10; ++i ) 
        std::cout << std::setw( 6 ) << std::setfill( ' ') << std::right << i;
    for( size_word i = 0; i != memory_size; ++i ) 
    {
        if( i % 10 == 0 )
            std::cout << "\n" << std::setw( 3 ) << std::setfill( ' ' ) << i << " ";
        std::cout << std::setw( 5 ) << std::setfill( '0' ) << std::showpos << std::internal << memory[ i ] << " ";
    }
    std::cout << std::endl;
}

SML::~SML()
{
    // resets all the registers
    set_registers();

    // free the memory
    delete [] memory;
}

Evaluator.h

#ifndef SML_EVALUATOR_H_
#define SML_EVALUATOR_H_

#include <iostream>
#include <stdint.h>

typedef int32_t size_word; 
constexpr size_word instruction_max_sixe = 70;

class SML;

class Evaluator
{
    public:
        Evaluator() = default;
        Evaluator( const SML & );
        int evaluate( SML &s, const int ins_reg, const int divisor );
    
    private:
        void read( SML &s, const int opr );
        void write( SML &s, const int opr );
        void read_str( SML &s, const int opr );
        void write_str( SML &s, const int opr );
        void load( SML &s, const int opr );
        void store( SML &s, const int opr );
        void add( SML &s, const int opr );
        void subtract( SML &s, const int opr );
        void multiply( SML &s, const int opr );
        void divide( SML &s, const int opr );
        void modulo( SML &s, const int opr );
        void branch( SML &s, const int opr );
        void branchneg( SML &s, const int opr );
        void branchzero( SML &s, const int opr );
        void newline( SML &s, const int opr );
        void smldebug( SML &s, const int opr );
        bool division_by_zero( SML &s, const int opr );

        void (Evaluator::*instruction_set[ instruction_max_sixe ])( SML &, int );
};

#endif

Evaluator.cpp

#include "evaluator.h"

#include "sml.h"

Evaluator::Evaluator( const SML &s )
{
    instruction_set[ s.read_ ] = &Evaluator::read;
    instruction_set[ s.write_ ] = &Evaluator::write;
    instruction_set[ s.read_str_ ] = &Evaluator::read_str;
    instruction_set[ s.write_str_ ] = &Evaluator::write_str;
    instruction_set[ s.load_ ] = &Evaluator::load;
    instruction_set[ s.store_ ] = &Evaluator::store;
    instruction_set[ s.add_ ] = &Evaluator::add;
    instruction_set[ s.subtract_ ] = &Evaluator::subtract;
    instruction_set[ s.multiply_ ] = &Evaluator::multiply;
    instruction_set[ s.divide_ ] = &Evaluator::divide;
    instruction_set[ s.modulo_ ] = &Evaluator::modulo;
    instruction_set[ s.branch_ ] = &Evaluator::branch;
    instruction_set[ s.branchneg_ ] = &Evaluator::branchneg;
    instruction_set[ s.branchzero_ ] = &Evaluator::branchzero;
    instruction_set[ s.newline_ ] = &Evaluator::newline;
    instruction_set[ s.sml_debug_ ] = &Evaluator::smldebug;
}

int Evaluator::evaluate( SML &s, const int ins_reg, const int divisor) 
{
    size_word &opr_code = s.registers[ static_cast<unsigned>( OPERATION_CODE ) ];
    size_word &opr = s.registers[ static_cast<unsigned>( OPERAND ) ];
    opr_code = ins_reg /  divisor;
    opr = ins_reg %  divisor;

    if( opr_code == s.halt_ )
        return 0;
    else 
        (this->*(instruction_set[ opr_code ]))( s, opr );
        return 1;
}

void Evaluator::read( SML &s, const int opr ) 
{  
    std::cin >> s.memory[ opr ];
}

void Evaluator::write( SML &s, const int opr ) 
{ 
    std::cout << s.memory[ opr ]; 
}

void Evaluator::read_str( SML &s, const int opr )
{
    std::cin >> s.temp_str;
    s.memory[ opr ] = s.temp_str.size();

    for( std::string::size_type i = 1; i != s.temp_str.size() + 1; ++i )
        s.memory[ opr + i ] = int( s.temp_str[ i - 1 ] );
}

void Evaluator::write_str( SML &s, const int opr )
{
    for( int i = 0; i != s.memory[ opr ] + 1; ++i )
        std::cout << char( s.memory[ opr + i ]);
}

void Evaluator::load( SML &s, const int opr )
{
    size_word &accumulator = s.registers[ static_cast<unsigned>( ACCUMULATOR ) ];
    accumulator = s.memory[ opr ];
}

void Evaluator::store( SML &s, const int opr )
{
    size_word &accumulator = s.registers[ static_cast<unsigned>( ACCUMULATOR ) ];
    s.memory[ opr ] = accumulator;
}

void Evaluator::add( SML &s, const int opr )
{
    size_word &accumulator = s.registers[ static_cast<unsigned>( ACCUMULATOR ) ];
    accumulator += s.memory[ opr ];
}

void Evaluator::subtract( SML &s, const int opr )
{
    size_word &accumulator = s.registers[ static_cast<unsigned>( ACCUMULATOR ) ];
    accumulator -= s.memory[ opr ];
}

void Evaluator::multiply( SML &s, const int opr )
{
    size_word &accumulator = s.registers[ static_cast<unsigned>( ACCUMULATOR ) ];
    accumulator *= s.memory[ opr ];
}

void Evaluator::divide( SML &s, const int opr )
{
    if( division_by_zero( s, opr ) )
        throw std::invalid_argument( "Division by zero: Program terminated abnormally." );

    size_word &accumulator = s.registers[ static_cast<unsigned>( ACCUMULATOR ) ];
    accumulator /= s.memory[ opr ];
}

void Evaluator::modulo( SML &s, const int opr )
{
    if( division_by_zero( s, opr ) )
        throw std::invalid_argument( "Division by zero: Program terminated abnormally." );

    size_word &accumulator = s.registers[ static_cast<unsigned>( ACCUMULATOR ) ];
    accumulator /= s.memory[ opr ];
}

bool Evaluator::division_by_zero( SML &s, const int opr )
{
    return ( s.memory[ opr ] == 0 );
}

void Evaluator::branchneg( SML &s, const int opr )
{
    size_word &accumulator = s.registers[ static_cast<unsigned>( ACCUMULATOR ) ];
    if( accumulator < 0 ) 
        branch( s, opr );
}

void Evaluator::branchzero( SML &s, const int opr )
{
    size_word &accumulator = s.registers[ static_cast<unsigned>( ACCUMULATOR ) ];
    if( accumulator == 0 ) 
        branch( s, opr );
}

void Evaluator::branch( SML &s, const int opr )
{
    size_word &ins_cnt = s.registers[ static_cast<unsigned>( INSTRUCTION_COUNTER ) ];
    s.registers[ static_cast<unsigned>( TEMPORARY_COUNTER ) ] = ins_cnt;
    ins_cnt = opr;
    s.execute();
    ins_cnt =  s.registers[ static_cast<unsigned>( TEMPORARY_COUNTER ) ];  
}

void Evaluator::newline( SML &s, const int opr )
{
    std::cout << '\n' << std::flush;
}

void Evaluator::smldebug( SML &s, const int opr )
{
    if ( opr == 1 ) s.debug = true;
    else if ( opr == 0 ) s.debug = false;
}

main.cpp

#include "sml.h"

int main()
{
    SML sml(1000, -999999, 999999 );
    sml.display_welcome_message();
    sml.load_program();
}

다음은 기계 테스트를 위해 작성된 지침입니다.

Tests

0xA60 // read a value and store in address 60( written to index 96(decimal) in the array, 
0xA61 // read another value and store in address 61
0x1460 // write the value stored in address 60 to the accumulator
0x1e61 // add the value stored in address 61 to the accumulator
0x320 // print a newline
0x1562 // store the value in the accumulatore to address 62
0xb62 // write the value in address 62 to the screen
0x320 // print a newline
0xc67 // read a string and store it size at address 67, the characters would be stored from 68 to end of character
0xd67 // write the characters to screen
0x2c1 // turn on debug
-0x1869f // start execution

답변

11 AryanParekh Nov 12 2020 at 14:55

몇 가지

를 사용하여 원시 문자열 리터럴

std::cout << "***" << " WELCOME TO SIMPLETRON! " << "***\n\n";
    std::cout << std::setw(5) << std::left << "***"
        << "Please enter your program one instruction"
        << std::setw(5) << std::right << "***\n";

    std::cout << std::setw(5) << std::left << "***"
        << "(or data word) at a time. I will type the"
        << std::setw(5) << std::right << "***\n";

    std::cout << std::setw(5) << std::left << "***"
        << "location number and a question mark (?)."
        << std::setw(6) << std::right << "***\n";

    std::cout << std::setw(5) << std::left << "***"
        << "You then type the word for that location"
        << std::setw(6) << std::right << "***\n";

    std::cout << std::setw(5) << std::left << "***"
        << "Type the sentinel -0x1869F to stop entering"
        << std::setw(5) << std::right << "***\n";

    std::cout << std::setw(5) << std::left << "***"
        << "your program"
        << std::setw(5) << std::right << "***";

    std::cout << "\n\n" << std::flush;

이것은 유지하기가 매우 어려울 수 있습니다. 간단하게 문자열 리터럴 을 사용하여 삶을 편하게 할 수 있습니다.

const char* welcome_msg = R"""(

***         WELCOME TO SIMPLETRON!               ***

***  Please enter your program one instruction   ***
***  (or data word) at a time. I will type the   ***
***  location number and a question mark (?).    ***
***  You then type the word for that location    ***
***  Type the sentinel -0x1869F to stop entering ***
***  your program                                ***

)"""
std::cout << welcome_msg;

단순화

    registers[static_cast<unsigned>(ACCUMULATOR)] = 0;
    registers[static_cast<unsigned>(INSTRUCTION_COUNTER)] = 0;
    registers[static_cast<unsigned>(TEMPORARY_COUNTER)] = 0;
    registers[static_cast<unsigned>(INSTRUCTION_REGISTER)] = 0;
    registers[static_cast<unsigned>(OPERATION_CODE)] = 0;
    registers[static_cast<unsigned>(OPERAND)] = 0;

에서 무언가를 사용할 때마다 unsigned로 캐스팅하는 대신 먼저 enum선언하지 않는 이유 는 무엇 unsigned입니까?

enum REGISTERS : unsigned
{
    ACCUMULATOR = 0,
    INSTRUCTION_COUNTER = 1,
    TEMPORARY_COUNTER = 2,
    INSTRUCTION_REGISTER = 3,
    OPERATION_CODE = 4,
    OPERAND = 5
};

또한 연속적이므로 여기에 값을 지정할 필요가 없습니다. 즉, 이것은

enum REGISTERS : unsigned
{
    ACCUMULATOR,
    INSTRUCTION_COUNTER ,
    TEMPORARY_COUNTER,
    INSTRUCTION_REGISTER,
    OPERATION_CODE,
    OPERAND
};

루프 사용

    registers[ACCUMULATOR] = 0;
    registers[INSTRUCTION_COUNTER] = 0;
    registers[TEMPORARY_COUNTER] = 0;
    registers[INSTRUCTION_REGISTER] = 0;
    registers[OPERATION_CODE] = 0;
    registers[OPERAND] = 0;

이것들은 모두 1에서 5까지 번호가 매겨져 있다는 사실을 활용하십시오.

    for (int i = ACCUMULATOR; i <= OPERAND; i++)
        registers[i] = 0;

비교 size_tint32_t

int32_t고정 너비는 32
size_t입니다. 플랫폼에 따라 32/64 비트입니다.
둘 다 자유롭게 비교하는 것은 때때로 위험 할 수 있습니다 .

s.memory[opr] = s.temp_str.size();
 in32_t       =    size_t

경우 size_t(가능, 가능성은 희박하지만은)의 최대 크기를 초과하는 int32_t오버 플로우를! 내가 좋아하는 것은와 같은 사용자 정의 매크로를 유지 한 _DEBUG_다음 #ifdef이를 확인 하는 데 사용 하는 것입니다.

#ifdef _DEBUG_
if ( s.temp_str.size() > INT32_MAX ) // handle it here

#endif // _DEBUG_
8 pacmaninbw Nov 12 2020 at 22:21

전반적인 관찰

여기 첫 번째 질문에 대해 심각한 개선이 있습니다. 이 두 번째 버전을 작성하는 것이 더 쉬웠습니까?

이 프로그램은 정확히 사용자 친화적이지 않습니다. SML 프로그램을 실행할 때 사용자에게 read명령문에 대한 입력을 요구하지 않습니다 .

C ++로 객체 지향 프로그래밍을하고 있는데 이것은 좋은 일입니다!

두 클래스 사이에는 다소 강한 종속성이있는 것으로 보이며 이는 긴밀한 결합 으로 알려져 있으며 일반적으로 객체 설계에 문제가 있음을 나타냅니다. 특수 출력이 필요한 클래스 friend에서 <<연산자 를 정의하는 것 외에는 적어도 27 년 동안 사용하지 않았습니다 . 수업의 책임은 더 잘 분류되어야합니다.

5 가지 SOLID 프로그래밍 원칙을 배우면 도움이 될 것 같습니다. SOLID 는 소프트웨어 설계를보다 이해하기 쉽고 유연하며 유지 보수하기 쉽게 만들기위한 5 가지 설계 원칙의 니모닉 약어입니다. 이렇게하면 개체와 클래스를 더 잘 디자인 할 수 있습니다.

  1. 단일 책임 원칙 - 클래스는 단지 클래스의 사양에 영향을 미칠 수 있어야 소프트웨어 사양의 일부를 변경, 단일 책임을 가져야한다. 이 특별한 원칙은 함수형 프로그래밍에도 적용될 수 있습니다.
  2. 오픈 폐쇄 원칙 - 확장을 위해 열려 있어야 소프트웨어 엔티티 (등 클래스, 모듈, 함수 등) 상태지만, 수정을 위해 마감했다.
  3. 리스 코프 치환 원칙은 - 프로그램의 객체는 그 프로그램의 정확성을 변경하지 않고 자신의 서브 타입의 인스턴스로 교체해야합니다.
  4. 인터페이스 분리의 원칙 - 어떤 클라이언트가 사용하지 않는 방법에 따라 강제되지 않는다는 것을 상태.
  5. 종속성 반전 원리 - 소프트웨어 모듈 디커플링의 특정 형태입니다. 이 원칙을 따를 때, 높은 수준의 정책 설정 모듈에서 낮은 수준의 종속성 모듈로 설정된 기존 종속성 관계가 역전되어 낮은 수준의 모듈 구현 세부 정보와 독립적으로 높은 수준의 모듈을 렌더링합니다.

프로세서를 나타내는 세 번째 클래스가있을 수 있습니다. 에 대한 인덱스에 대해 SML과 Evaluator 모두에서 공유하는 열거 형을 만들 수도 있습니다 instruction_set.

복잡성 void SML::memory_dump() const

보면 void SML::memory_dump() const경우 실제로 2 개 개의 분리 기능을 참조 단일 책임 원칙이 적용됩니다

  1. dump_registers ()
  2. dump_memory ()

두 함수를 모두 포함하는 외부 함수는 dump_current_program_state().

void SML::dump_current_program_state() const
{
    dump_registers();
    memory_dump();
}

void SML::dump_registers() const
{
    std::cout << "\nREGISTERS:\n";

    std::cout << std::setw(25) << std::left << std::setfill(' ') << "accumulator" << std::showpos
        << std::setw(5) << std::setfill('0') << std::internal << registers[0] << '\n';

    std::cout << std::setw(28) << std::left << std::setfill(' ')
        << "instruction counter" << std::noshowpos << std::setfill('0')
        << std::right << std::setw(2) << registers[1] << '\n';

    std::cout << std::setw(25) << std::left << std::setfill(' ')
        << "instruction register" << std::showpos << std::setw(5) << std::setfill('0')
        << std::internal << registers[3] << '\n';

    std::cout << std::setw(28) << std::left << std::setfill(' ')
        << "operation code" << std::noshowpos << std::setfill('0')
        << std::right << std::setw(2) << registers[4] << '\n';

    std::cout << std::setw(28) << std::left << std::setfill(' ')
        << "operand" << std::noshowpos << std::setfill('0')
        << std::right << std::setw(2) << registers[5] << '\n';
}

void SML::memory_dump() const
{
    std::cout << "\n\nMEMORY:\n";
    std::cout << "  ";

    for (int i = 0; i != 10; ++i)
        std::cout << std::setw(6) << std::setfill(' ') << std::right << i;
    for (size_word i = 0; i != memory_size; ++i)
    {
        if (i % 10 == 0)
            std::cout << "\n" << std::setw(3) << std::setfill(' ') << i << " ";
        std::cout << std::setw(5) << std::setfill('0') << std::showpos << std::internal << memory[i] << " ";
    }
    std::cout << std::endl;
}

매직 넘버

sml.h에서 매직 넘버를 방지하는 일을 잘했지만, main()함수 (1000, -999999, 999999)와 SML::memory_dump()(25, 5, 28, 10) 에도 매직 넘버 가 있습니다. 코드를 더 읽기 쉽고 유지 관리하기 쉽게 만들기 위해 기호 상수를 만듭니다. 이 번호는 여러 곳에서 사용할 수 있으며 한 줄만 편집하여 변경할 수 있으므로 유지 관리가 더 쉬워집니다.

에서 main()만들 수있는 constexpr memory_size = 1000;첫 번째 값, 나는하지 확인 -999999 9999999 값을 호출해야하는지입니다.

초기화되지 않은 레지스터

다음 생성자에서 레지스터가 초기화되는 위치를 볼 수 없습니다.

SML::SML(SML&& s)
{
    swap(*this, s);
    memory = new size_word[memory_size];
    std::move(s.memory, s.memory + s.memory_size, memory);
}
8 AlexShirley Nov 13 2020 at 00:21

enum class열거 형 대신 사용

열거 형을 enum class. 가치가 상충되는 유사하거나 동일하게 명명 된 상태를 사용하는 두 개 이상의 상태 머신을 몇 번이나 풀어야했는지 말할 수 없습니다. 이렇게하면 체크되지 않은 값을 레지스터로 전달하는 것을 방지 할 수 있습니다.

표준 용기 사용

귀하의 memory변수가 될 수있다 std::vector- SML 객체가 소멸 될 때 다음의 ctor 동안 크기를 예약하고, 자동으로 청소합니다.

마찬가지로 std::array.NET 용 맵 중 하나를 사용할 수 있습니다 registers. std::arrayconstexpr로 만들 수 있으므로 c++2a/로 컴파일 c++20하면 런타임 대신 컴파일시 전체 프로그램을 잠재적으로 확인할 수 있습니다.

이 두 가지 모두 복사 및 이동 연산자를 좀 더 쉽게 길들이도록 만들어야합니다. 게다가

표준 알고리즘 사용

특히 Evaluator표준 알고리즘으로 연습 할 수 있습니다. 이것이 반드시 속도 향상은 아니지만 좋은 습관입니다.

void Evaluator::write_str( SML &s, const int opr )
{
    for( int i = 0; i != s.memory[ opr ] + 1; ++i )
        std::cout << char( s.memory[ opr + i ]);
}

void Evaluator::write_str(SML &s, const Operand o){
    auto out_itr = std::ostream_iterator<char>(std::cout, "");
    std::copy(s.memory.cbegin(), std::next(s.memory.cbegin() to_underlying(O)), out_itr);
}

알고리즘을 계속 사용하면 추가적인 이점은 일관성과 의도 전달입니다. 당신이 finding무언가를 사용 find하거나 인쇄와 같은 항목 find_if을 수행하는 경우 . 당신은 또한 표준 알고리즘을 다시 만들 수 있습니다. 그들은 당신의 발가락을 아주 쉽게 담그기 쉬운 초보적인 템플릿 함수입니다.for eachfor_each

다른 곳에 정의 됨 enum class-int 로 변환

#include <type_traits>

template <typename E>
constexpr auto to_underlying(E e) noexcept
{
    return static_cast<std::underlying_type_t<E>>(e);
}

std::ostream멤버를 추가하고 대신 파이프를 통해std::cout

이것은 먼 길을가는 약간의 멋짐입니다. std::ostream클래스에 멤버를 추가 한 다음 기본값으로 구성 std::cout하면 원하는대로 출력 할 수 있습니다! 파이프 할 파일이 있습니까? 큰. 단위 테스트가 가능한 스트림은 어떻습니까? 확실한. 이 작업을 수행하면 자동 빌드 및 테스트를 추가하여 작은 변경으로 인해 실제로 모든 것이 손상되었는지 수동으로 확인해야하는 시간을 절약 할 수 있습니다.

unique_ptr 사용

보너스 편집 : 이것에 대해 기억했기 때문에 표준 컨테이너를 사용하지 않으려면 unique_ptrs로 데이터 (레지스터 및 메모리)를 실제로 관리해야합니다. new그리고 delete종종 코드 냄새, 그리고 좋은 이유로 처리됩니다. 그것은하려고 정말 쉽게 double-free하거나 잊어 delete메모리 누수가 ,이 모두는 아주 나쁜 있습니다.