기계어 시뮬레이터
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
답변
몇 가지
를 사용하여 원시 문자열 리터럴
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_t및int32_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_
전반적인 관찰
여기 첫 번째 질문에 대해 심각한 개선이 있습니다. 이 두 번째 버전을 작성하는 것이 더 쉬웠습니까?
이 프로그램은 정확히 사용자 친화적이지 않습니다. SML 프로그램을 실행할 때 사용자에게 read명령문에 대한 입력을 요구하지 않습니다 .
C ++로 객체 지향 프로그래밍을하고 있는데 이것은 좋은 일입니다!
두 클래스 사이에는 다소 강한 종속성이있는 것으로 보이며 이는 긴밀한 결합 으로 알려져 있으며 일반적으로 객체 설계에 문제가 있음을 나타냅니다. 특수 출력이 필요한 클래스 friend에서 <<연산자 를 정의하는 것 외에는 적어도 27 년 동안 사용하지 않았습니다 . 수업의 책임은 더 잘 분류되어야합니다.
5 가지 SOLID 프로그래밍 원칙을 배우면 도움이 될 것 같습니다. SOLID 는 소프트웨어 설계를보다 이해하기 쉽고 유연하며 유지 보수하기 쉽게 만들기위한 5 가지 설계 원칙의 니모닉 약어입니다. 이렇게하면 개체와 클래스를 더 잘 디자인 할 수 있습니다.
- 단일 책임 원칙 - 클래스는 단지 클래스의 사양에 영향을 미칠 수 있어야 소프트웨어 사양의 일부를 변경, 단일 책임을 가져야한다. 이 특별한 원칙은 함수형 프로그래밍에도 적용될 수 있습니다.
- 오픈 폐쇄 원칙 - 확장을 위해 열려 있어야 소프트웨어 엔티티 (등 클래스, 모듈, 함수 등) 상태지만, 수정을 위해 마감했다.
- 리스 코프 치환 원칙은 - 프로그램의 객체는 그 프로그램의 정확성을 변경하지 않고 자신의 서브 타입의 인스턴스로 교체해야합니다.
- 인터페이스 분리의 원칙 - 어떤 클라이언트가 사용하지 않는 방법에 따라 강제되지 않는다는 것을 상태.
- 종속성 반전 원리 - 소프트웨어 모듈 디커플링의 특정 형태입니다. 이 원칙을 따를 때, 높은 수준의 정책 설정 모듈에서 낮은 수준의 종속성 모듈로 설정된 기존 종속성 관계가 역전되어 낮은 수준의 모듈 구현 세부 정보와 독립적으로 높은 수준의 모듈을 렌더링합니다.
프로세서를 나타내는 세 번째 클래스가있을 수 있습니다. 에 대한 인덱스에 대해 SML과 Evaluator 모두에서 공유하는 열거 형을 만들 수도 있습니다 instruction_set.
복잡성 void SML::memory_dump() const
보면 void SML::memory_dump() const경우 실제로 2 개 개의 분리 기능을 참조 단일 책임 원칙이 적용됩니다
- dump_registers ()
- 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);
}
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및 메모리 누수가 ,이 모두는 아주 나쁜 있습니다.