Makine Dili Simülatörü
Simple Machine Translator (SML), onaltılık olarak yazılan kodu yürüten bir simülatördür. Okuma, yazma, ekleme, çıkarma ve daha pek çok özelliği destekler. Bu minimal egzersiz bağlantısıyla ilgili önceki sorum , takip etmek isteyenler için burada bulunabilir . Çok fazla değişiklik yaptım, yeniden yapılandırdım ve bir şeyleri hareket ettirdim ve bir incelemeyi takdir ediyorum.
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();
}
Makineyi test etmek için yazılan talimatlar aşağıdadır
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
Yanıtlar
Sadece birkaç şey
Ham dize değişmezlerini kullan
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;
Bunun sürdürülmesi son derece zor olabilir. Hayatınızı kolaylaştırmak için dizeleri basitçe kullanabilirsiniz
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;
Basitleştirin
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;
'Den bir şeyi her kullandığınızda imzasız olarak yayınlamak yerine enum, neden unsignedönce ilan etmiyorsunuz ?
enum REGISTERS : unsigned
{
ACCUMULATOR = 0,
INSTRUCTION_COUNTER = 1,
TEMPORARY_COUNTER = 2,
INSTRUCTION_REGISTER = 3,
OPERATION_CODE = 4,
OPERAND = 5
};
Ayrıca, sürekli olduklarından burada değerleri belirtmeniz gerekmez. Bu aynı olduğu anlamına gelir
enum REGISTERS : unsigned
{
ACCUMULATOR,
INSTRUCTION_COUNTER ,
TEMPORARY_COUNTER,
INSTRUCTION_REGISTER,
OPERATION_CODE,
OPERAND
};
Bir döngü kullanın
registers[ACCUMULATOR] = 0;
registers[INSTRUCTION_COUNTER] = 0;
registers[TEMPORARY_COUNTER] = 0;
registers[INSTRUCTION_REGISTER] = 0;
registers[OPERATION_CODE] = 0;
registers[OPERAND] = 0;
Bunların hepsinin 1'den 5'e kadar numaralandırılmasından yararlanın.
for (int i = ACCUMULATOR; i <= OPERAND; i++)
registers[i] = 0;
Karşılaştırma size_tveint32_t
int32_t32 sabit genişliğe sahiptir
size_t. platforma bağlı olarak 32/64 bittir.
İkisini de özgürce karşılaştırmak bazen tehlikeli olabilir .
s.memory[opr] = s.temp_str.size();
in32_t = size_t
Eğer size_t(çok olası olmasa da) maksimum boyutunu aşarsa int32_t, taşma! Yapmaktan hoşlandığım şey, özel bir makroyu saklamak _DEBUG_ve ardından #ifdefbunu kontrol etmek için kullanmaktır .
#ifdef _DEBUG_
if ( s.temp_str.size() > INT32_MAX ) // handle it here
#endif // _DEBUG_
Genel Gözlemler
Burada ilk soruda ciddi bir gelişme görüyorum. Bu ikinci versiyonu yazmayı daha kolay buldunuz mu?
Program tam olarak kullanıcı dostu değildir, SML programını çalıştırırken kullanıcıdan readifadeler üzerinde girdi istemez .
C ++ 'da nesne yönelimli programlamanız üzerinde çalışıyorsunuz ve bu iyi bir şey!
2 sınıf arasında oldukça güçlü bağımlılıklar var gibi görünüyor, bu sıkı bağlantı olarak bilinir ve genellikle nesnelerin tasarımında bir sorun olduğunu gösterir. Ben kullanmadıysanız friendtanımlayan hariç en az 27 yıl içinde <<uzman çıkışı ihtiyacım sınıflarında operatörü. Sınıfların sorumluluklarının daha iyi bölümlere ayrılması gerekiyor.
Bu noktada, 5 SOLID programlama ilkesini öğrenmenizin faydalı olacağını düşünüyorum. SOLID , yazılım tasarımlarını daha anlaşılır, esnek ve sürdürülebilir hale getirmeyi amaçlayan beş tasarım ilkesinin kısaltmasıdır. Bu, nesnelerinizi ve sınıflarınızı daha iyi tasarlamanıza yardımcı olacaktır.
- Tek Sorumluluk Prensibi - Bir sınıf yalnızca tek sınıfın şartname etkilemesi mümkün olmalıdır yazılımın şartnamenin bir parçası olarak değişir, tek bir sorumluluğa sahip olmalıdır. Bu özel ilke, fonksiyonel programlamaya da uygulanabilir.
- Açık kapalı İlke - uzantısı için açık olmalıdır yazılım varlıkları (vb sınıflar, modüller, fonksiyonlar,) devletler, ancak değişiklik için kapandı.
- Liskov değiştirme İlke - Bir programda nesneler o programın doğruluğunu değiştirmeden kendi alt türleri örnekleri ile değiştirilebilir olmalıdır.
- Arayüz ayrımı ilkesi - hayır istemci kullanmaz yöntemlere bağlıdır zorunda gerektiğini belirtir.
- Bağımlılık Inversion Prensibi - yazılım modüllerini ayırma belirli bir formudur. Bu prensibi takip ederken, yüksek seviyeli, politika belirleyen modüllerden düşük seviyeli bağımlılık modüllerine kadar kurulan geleneksel bağımlılık ilişkileri tersine çevrilir, böylece yüksek seviyeli modüller düşük seviyeli modül uygulama detaylarından bağımsız hale gelir.
İşlemciyi temsil eden üçüncü bir sınıf olabilir. Ayrıca, içindeki dizinler için hem SML hem de Değerlendirici tarafından paylaşılan bir enum oluşturabilirsiniz instruction_set.
Karmaşıklığı void SML::memory_dump() const
Baktığımda Tek Sorumluluk İlkesi uygulandığında void SML::memory_dump() constaslında 2 ayrı işlev görüyorum
- dump_registers ()
- dump_memory ()
Her iki işlevi içeren dış işlev olabilir 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;
}
Sihirli Sayılar
Sml.h'de sihirli sayıları önlemekte iyi bir iş çıkardınız, ancak main()işlevde (1000, -999999, 999999) ve SML::memory_dump()(25, 5, 28, 10) içinde Sihirli Sayılar var, daha iyi olabilir kodu daha okunaklı ve bakımı daha kolay hale getirmek için sembolik sabitler oluşturmak. Bu numaralar pek çok yerde kullanılabilir ve sadece bir satırda düzenleme yapılarak değiştirilebilmesi bakımı kolaylaştırır.
Gelen main()oluşturabilir constexpr memory_size = 1000;ilk değeri, ben emin -999999 ve 9999999 değerleri çağrılmalıdır kişilerden biriyim.
İlklendirilmemiş Kayıtlar
Aşağıdaki kurucuda yazmaçların nerede başlatıldığını görmüyorum:
SML::SML(SML&& s)
{
swap(*this, s);
memory = new size_word[memory_size];
std::move(s.memory, s.memory + s.memory_size, memory);
}
enum classEnum yerine kullanın
Numaralamayı a yapmak iyi bir alışkanlıktır enum class. Değer açısından çelişen benzer veya aynı şekilde adlandırılmış durumları kullanan iki veya daha fazla durum makinesini kaç kez çözmem gerektiğini size söyleyemem. Bu, denetlenmemiş değerleri kayıt olarak geçirmenizi önleyecektir.
Standart kaplar kullanın
Sizin memorydeğişken bir olabilir std::vector- sml nesne yok edilir sonra ctor sırasında boyutunu saklıdır ve otomatik temizlenene.
Aynı şekilde, std::arrayiçin haritalardan birini veya birini kullanabilirsiniz registers. std::arrayconstexpr yapılabilir, bu nedenle c++2a/ ile derlerseniz, c++20potansiyel olarak tüm programınızı çalışma zamanı yerine derleme sırasında doğrulayabilirsiniz.
Bunların her ikisi de kopyalama ve taşıma operatörlerini evcilleştirmeyi biraz daha kolaylaştırmalıdır. Aynı zamanda
Standart Algoritmaları Kullanın
Özellikle de Evaluatorstandart algoritmalarla alıştırma yapabilirsiniz. Bu mutlaka bir hızlandırma değildir, ancak iyi bir uygulamadır.
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);
}
Algoritmaları sürekli kullanmanın ek bir yararı da tutarlılık ve amacı iletmektir. Eğer bir şey iseniz finding, kullanın findveya bir find_ifşey yapıyorsanız for each, örneğin çıktı almak gibi, kullanabilirsiniz for_each. Ayrıca standart algoritmaları yeniden oluşturabilirsiniz, bunlar oldukça kolay giriş seviyesi şablon işlevleridir, ayak parmaklarınızı eğmek oldukça kolaydır.
Başka yerde tanımlanmış - enum classint'e dönüştürmek için
#include <type_traits>
template <typename E>
constexpr auto to_underlying(E e) noexcept
{
return static_cast<std::underlying_type_t<E>>(e);
}
Bir std::ostreamüye ekleyin ve bunun yerine boru hattı oluşturunstd::cout
Bu uzun bir yol kat eden küçük bir hoşluk. std::ostreamSınıflarınıza üye ekleyerek ve ardından varsayılan olarak oluşturarak std::cout, daha sonra istediğiniz gibi çıktı verebilirsiniz! Aktarmak istediğiniz bir dosyanız var mı? Harika. Birim testi yapılabilen bir akışa ne dersiniz? Elbette. Bunu yaptıktan sonra, otomatik oluşturma ve test etme ekleyebilir, yaptığınız bu küçük değişikliğin aslında her şeyi bozup bozmadığını manuel olarak kontrol etme ihtiyacından zamandan tasarruf edebilirsiniz.
Unique_ptr kullan
Bonus düzenleme: Bunu hatırladığımdan beri - standart kapsayıcıları kullanmak istemiyorsanız, verilerinizi (kayıtlar ve bellek) unique_ptrs ile gerçekten yönetmelisiniz. newve deletegenellikle kod kokusu olarak ve iyi bir sebeple ele alınır. Bu denemek için gerçekten çok kolay double-freeveya unutmak deleteve bellek sızdırıyor , bunların ikisi de çok kötü.